commit b5859f4fa8bfb01c8c843f1a09b568807e5abd31 Author: 李令之 <2879174526@qq.com> Date: Mon May 27 20:50:22 2024 +0800 李令之 diff --git a/Application111.zip b/Application111.zip new file mode 100644 index 0000000..c7d38ef Binary files /dev/null and b/Application111.zip differ diff --git a/Application111/.gitignore b/Application111/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/Application111/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/Application111/.idea/.gitignore b/Application111/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/Application111/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/Application111/.idea/compiler.xml b/Application111/.idea/compiler.xml new file mode 100644 index 0000000..b589d56 --- /dev/null +++ b/Application111/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Application111/.idea/deploymentTargetDropDown.xml b/Application111/.idea/deploymentTargetDropDown.xml new file mode 100644 index 0000000..0c0c338 --- /dev/null +++ b/Application111/.idea/deploymentTargetDropDown.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/Application111/.idea/gradle.xml b/Application111/.idea/gradle.xml new file mode 100644 index 0000000..0897082 --- /dev/null +++ b/Application111/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/Application111/.idea/migrations.xml b/Application111/.idea/migrations.xml new file mode 100644 index 0000000..f8051a6 --- /dev/null +++ b/Application111/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/Application111/.idea/misc.xml b/Application111/.idea/misc.xml new file mode 100644 index 0000000..8978d23 --- /dev/null +++ b/Application111/.idea/misc.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/Application111/app/.gitignore b/Application111/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/Application111/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/Application111/app/build.gradle.kts b/Application111/app/build.gradle.kts new file mode 100644 index 0000000..4b3e6ee --- /dev/null +++ b/Application111/app/build.gradle.kts @@ -0,0 +1,39 @@ +plugins { + id("com.android.application") +} + +android { + namespace = "com.example.application111" + compileSdk = 34 + + defaultConfig { + applicationId = "com.example.application111" + minSdk = 24 + targetSdk = 34 + versionCode = 1 + versionName = "1.0" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } +} + +dependencies { + + implementation("androidx.appcompat:appcompat:1.6.1") + implementation("com.google.android.material:material:1.9.0") + implementation("androidx.constraintlayout:constraintlayout:2.1.4") + testImplementation("junit:junit:4.13.2") + androidTestImplementation("androidx.test.ext:junit:1.1.5") + androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") +} \ No newline at end of file diff --git a/Application111/app/proguard-rules.pro b/Application111/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/Application111/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/Application111/app/src/androidTest/java/com/example/application111/ExampleInstrumentedTest.java b/Application111/app/src/androidTest/java/com/example/application111/ExampleInstrumentedTest.java new file mode 100644 index 0000000..d7b4866 --- /dev/null +++ b/Application111/app/src/androidTest/java/com/example/application111/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.example.application111; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertEquals("com.example.application111", appContext.getPackageName()); + } +} \ No newline at end of file diff --git a/Application111/app/src/main/AndroidManifest.xml b/Application111/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..363b8ca --- /dev/null +++ b/Application111/app/src/main/AndroidManifest.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Application111/app/src/main/java/com/example/application111/MainActivity.java b/Application111/app/src/main/java/com/example/application111/MainActivity.java new file mode 100644 index 0000000..d5f7e7a --- /dev/null +++ b/Application111/app/src/main/java/com/example/application111/MainActivity.java @@ -0,0 +1,49 @@ +package com.example.application111; + + import androidx.appcompat.app.AppCompatActivity; + import androidx.fragment.app.FragmentManager; + import androidx.fragment.app.FragmentTransaction; + + import android.os.Bundle; + import android.view.View; + import android.widget.TextView; + +public class MainActivity extends AppCompatActivity implements View.OnClickListener { + //创建需要用到的控件的变量 + private TextView tv1,tv2; + private FragmentManager fm; + private FragmentTransaction ft; + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + //绑定控件 + tv1=(TextView)findViewById(R.id.menu1); + tv2=(TextView)findViewById(R.id.menu2); + //设置监听器,固定写法 + tv1.setOnClickListener(this); + tv2.setOnClickListener(this); + //若是继承FragmentActivity,fm=getFragmentManger(); + fm=getSupportFragmentManager(); + //fm可以理解为Fragment显示的管理者,ft就是它的改变者 + ft=fm.beginTransaction(); + //默认情况下就显示frag1 + ft.replace(R.id.content,new frag1()); + //提交改变的内容 + ft.commit(); + } + @Override + //控件的点击事件 + public void onClick(View v){ + ft=fm.beginTransaction(); + //切换选项卡 + int id = v.getId(); + if (id == R.id.menu1) { + ft.replace(R.id.content, new frag1()); + } else if (id == R.id.menu2) { + ft.replace(R.id.content, new frag2()); + } + ft.commit(); + } +} + diff --git a/Application111/app/src/main/java/com/example/application111/MusicActivity.java b/Application111/app/src/main/java/com/example/application111/MusicActivity.java new file mode 100644 index 0000000..d537e88 --- /dev/null +++ b/Application111/app/src/main/java/com/example/application111/MusicActivity.java @@ -0,0 +1,192 @@ +package com.example.application111; + +import androidx.annotation.RequiresApi; +import androidx.appcompat.app.AppCompatActivity; + +import android.animation.ObjectAnimator; +import android.content.ComponentName; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.view.View; +import android.view.animation.LinearInterpolator; +import android.widget.ImageView; +import android.widget.SeekBar; +import android.widget.TextView; + +import static java.lang.Integer.parseInt; + +public class MusicActivity extends AppCompatActivity implements View.OnClickListener{ + //进度条 + private static SeekBar sb; + private static TextView tv_progress,tv_total,name_song; + //动画 + private ObjectAnimator animator; + private MusicService.MusicControl musicControl; + private String name; + private Intent intent1,intent2; + private MyServiceConn conn; + //记录服务是否被解绑,默认没有 + private boolean isUnbind =false; + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_music); + //获取从frag1传来的信息 + intent1=getIntent(); + init(); + } + private void init(){ + //进度条上小绿点的位置,也就是当前已播放时间 + tv_progress=(TextView)findViewById(R.id.tv_progress); + //进度条的总长度,就是总时间 + tv_total=(TextView)findViewById(R.id.tv_total); + //进度条的控件 + sb=(SeekBar)findViewById(R.id.sb); + //歌曲名显示的控件 + name_song=(TextView)findViewById(R.id.song_name); + //绑定控件的同时设置点击事件监听器 + findViewById(R.id.btn_play).setOnClickListener(this); + findViewById(R.id.btn_pause).setOnClickListener(this); + findViewById(R.id.btn_continue_play).setOnClickListener(this); + findViewById(R.id.btn_exit).setOnClickListener(this); + + name=intent1.getStringExtra("name"); + name_song.setText(name); + //创建一个意图对象,是从当前的Activity跳转到Service + intent2=new Intent(this,MusicService.class); + conn=new MyServiceConn();//创建服务连接对象 + bindService(intent2,conn,BIND_AUTO_CREATE);//绑定服务 + //为滑动条添加事件监听,每个控件不同果然点击事件方法名都不同 + sb.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { + //这一行注解是保证API在KITKAT以上的模拟器才能顺利运行,也就是19以上 + @RequiresApi(api = Build.VERSION_CODES.KITKAT) + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + //进当滑动条到末端时,结束动画 + if (progress==seekBar.getMax()){ + animator.pause();//停止播放动画 + } + } + + @Override + //滑动条开始滑动时调用 + public void onStartTrackingTouch(SeekBar seekBar) { + } + @Override + //滑动条停止滑动时调用 + public void onStopTrackingTouch(SeekBar seekBar) { + //根据拖动的进度改变音乐播放进度 + int progress=seekBar.getProgress();//获取seekBar的进度 + musicControl.seekTo(progress);//改变播放进度 + } + }); + //声明并绑定音乐播放器的iv_music控件 + ImageView iv_music=(ImageView)findViewById(R.id.iv_music); + String position= intent1.getStringExtra("position"); + //praseInt()就是将字符串变成整数类型 + int i=parseInt(position); + iv_music.setImageResource(frag1.icons[i]); + //rotation和0f,360.0f就设置了动画是从0°旋转到360° + animator=ObjectAnimator.ofFloat(iv_music,"rotation",0f,360.0f); + animator.setDuration(10000);//动画旋转一周的时间为10秒 + animator.setInterpolator(new LinearInterpolator());//匀速 + animator.setRepeatCount(-1);//-1表示设置动画无限循环 + } + //handler机制,可以理解为线程间的通信,我获取到一个信息,然后把这个信息告诉你,就这么简单 + public static Handler handler=new Handler(){//创建消息处理器对象 + //在主线程中处理从子线程发送过来的消息 + @Override + public void handleMessage(Message msg){ + Bundle bundle=msg.getData();//获取从子线程发送过来的音乐播放进度 + //获取当前进度currentPosition和总时长duration + int duration=bundle.getInt("duration"); + int currentPosition=bundle.getInt("currentPosition"); + //对进度条进行设置 + sb.setMax(duration); + sb.setProgress(currentPosition); + //歌曲是多少分钟多少秒钟 + int minute=duration/1000/60; + int second=duration/1000%60; + String strMinute=null; + String strSecond=null; + if(minute<10){//如果歌曲的时间中的分钟小于10 + strMinute="0"+minute;//在分钟的前面加一个0 + }else{ + strMinute=minute+""; + } + if (second<10){//如果歌曲中的秒钟小于10 + strSecond="0"+second;//在秒钟前面加一个0 + }else{ + strSecond=second+""; + } + //这里就显示了歌曲总时长 + tv_total.setText(strMinute+":"+strSecond); + //歌曲当前播放时长 + minute=currentPosition/1000/60; + second=currentPosition/1000%60; + if(minute<10){//如果歌曲的时间中的分钟小于10 + strMinute="0"+minute;//在分钟的前面加一个0 + }else{ + strMinute=minute+" "; + } + if (second<10){//如果歌曲中的秒钟小于10 + strSecond="0"+second;//在秒钟前面加一个0 + }else{ + strSecond=second+" "; + } + //显示当前歌曲已经播放的时间 + tv_progress.setText(strMinute+":"+strSecond); + } + }; + //用于实现连接服务,比较模板化,不需要详细知道内容 + class MyServiceConn implements ServiceConnection{ + @Override + public void onServiceConnected(ComponentName name, IBinder service){ + musicControl=(MusicService.MusicControl) service; + } + @Override + public void onServiceDisconnected(ComponentName name){ + + } + } + //判断服务是否被解绑 + private void unbind(boolean isUnbind){ + //如果解绑了 + if(!isUnbind){ + musicControl.pausePlay();//音乐暂停播放 + unbindService(conn);//解绑服务 + } + } + @RequiresApi(api = Build.VERSION_CODES.KITKAT) + @Override + public void onClick(View v) { + int id = v.getId(); + if (id == R.id.btn_play) {//播放按钮点击事件 + String position = intent1.getStringExtra("position"); + int i = parseInt(position); + musicControl.play(i); + animator.start(); + } else if (id == R.id.btn_pause) {//暂停按钮点击事件 + musicControl.pausePlay(); + animator.pause(); + } else if (id == R.id.btn_continue_play) {//继续播放按钮点击事件 + musicControl.continuePlay(); + animator.start(); + } else if (id == R.id.btn_exit) {//退出按钮点击事件 + unbind(isUnbind); + isUnbind = true; + finish(); + } + } + @Override + protected void onDestroy(){ + super.onDestroy(); + unbind(isUnbind);//解绑服务 + } +} + diff --git a/Application111/app/src/main/java/com/example/application111/MusicService.java b/Application111/app/src/main/java/com/example/application111/MusicService.java new file mode 100644 index 0000000..cda86be --- /dev/null +++ b/Application111/app/src/main/java/com/example/application111/MusicService.java @@ -0,0 +1,95 @@ +package com.example.application111; + +import android.app.Service; +import android.content.Intent; +import android.media.MediaPlayer; +import android.net.Uri; +import android.os.Binder; +import android.os.Bundle; +import android.os.IBinder; +import android.os.Message; + +import java.util.Timer; +import java.util.TimerTask; +//这是一个Service服务类 +public class MusicService extends Service { + //声明一个MediaPlayer引用 + private MediaPlayer player; + //声明一个计时器引用 + private Timer timer; + //构造函数 + public MusicService() {} + @Override + public IBinder onBind(Intent intent){ + return new MusicControl(); + } + @Override + public void onCreate(){ + super.onCreate(); + //创建音乐播放器对象 + player=new MediaPlayer(); + } + //添加计时器用于设置音乐播放器中的播放进度条 + public void addTimer(){ + //如果timer不存在,也就是没有引用实例 + if(timer==null){ + //创建计时器对象 + timer=new Timer(); + TimerTask task=new TimerTask() { + @Override + public void run() { + if (player==null) return; + int duration=player.getDuration();//获取歌曲总时长 + int currentPosition=player.getCurrentPosition();//获取播放进度 + Message msg= MusicActivity.handler.obtainMessage();//创建消息对象 + //将音乐的总时长和播放进度封装至bundle中 + Bundle bundle=new Bundle(); + bundle.putInt("duration",duration); + bundle.putInt("currentPosition",currentPosition); + //再将bundle封装到msg消息对象中 + msg.setData(bundle); + //最后将消息发送到主线程的消息队列 + MusicActivity.handler.sendMessage(msg); + } + }; + //开始计时任务后的5毫秒,第一次执行task任务,以后每500毫秒(0.5s)执行一次 + timer.schedule(task,5,500); + } + } + //Binder是一种跨进程的通信方式 + class MusicControl extends Binder{ + public void play(int i){//String path + Uri uri=Uri.parse("android.resource://"+getPackageName()+"/raw/"+"music"+i); + try{ + //重置音乐播放器 + player.reset(); + //加载多媒体文件 + player=MediaPlayer.create(getApplicationContext(),uri); + player.start();//播放音乐 + addTimer();//添加计时器 + }catch(Exception e){ + e.printStackTrace(); + } + } + //下面的暂停继续和退出方法全部调用的是MediaPlayer自带的方法 + public void pausePlay(){ + player.pause();//暂停播放音乐 + } + public void continuePlay(){ + player.start();//继续播放音乐 + } + public void seekTo(int progress){ + player.seekTo(progress);//设置音乐的播放位置 + } + } + //销毁多媒体播放器 + @Override + public void onDestroy(){ + super.onDestroy(); + if(player==null) return; + if(player.isPlaying()) player.stop();//停止播放音乐 + player.release();//释放占用的资源 + player=null;//将player置为空 + } +} + diff --git a/Application111/app/src/main/java/com/example/application111/frag1.java b/Application111/app/src/main/java/com/example/application111/frag1.java new file mode 100644 index 0000000..a6b6d1a --- /dev/null +++ b/Application111/app/src/main/java/com/example/application111/frag1.java @@ -0,0 +1,69 @@ +package com.example.application111; + +import android.content.Intent; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.TextView; + +import androidx.fragment.app.Fragment; + +public class frag1 extends Fragment { + private View view; + //创建歌曲的String数组和歌手图片的int数组 + public String[] name={"F.I.R——千年之恋","MoreanP——暮秋沉眠","薛凯琪,方大同——复刻回忆"}; + public static int[] icons={R.drawable.music0,R.drawable.music1,R.drawable.music2}; + @Override + public View onCreateView(final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){ + //绑定布局,只不过这里是用inflate()方法 + view=inflater.inflate(R.layout.music_list,null); + //创建listView列表并且绑定控件 + ListView listView=view.findViewById(R.id.lv); + //实例化一个适配器 + MyBaseAdapter adapter=new MyBaseAdapter(); + //列表设置适配器 + listView.setAdapter(adapter); + //列表元素的点击监听器 + listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + //创建Intent对象,参数就是从frag1跳转到MusicActivity + Intent intent=new Intent(frag1.this.getContext(), MusicActivity.class); + //将歌曲名和歌曲的下标存入Intent对象 + intent.putExtra("name",name[position]); + intent.putExtra("position",String.valueOf(position)); + //开始跳转 + startActivity(intent); + } + }); + return view; + } + //这里是创建一个自定义适配器,可以作为模板 + class MyBaseAdapter extends BaseAdapter{ + @Override + public int getCount(){return name.length;} + @Override + public Object getItem(int i){return name[i];} + @Override + public long getItemId(int i){return i;} + + @Override + public View getView(int i ,View convertView, ViewGroup parent) { + //绑定好VIew,然后绑定控件 + View view=View.inflate(frag1.this.getContext(),R.layout.item_layout,null); + TextView tv_name=view.findViewById(R.id.item_name); + ImageView iv=view.findViewById(R.id.iv); + //设置控件显示的内容,就是获取的歌曲名和歌手图片 + tv_name.setText(name[i]); + iv.setImageResource(icons[i]); + return view; + } + } + +} + diff --git a/Application111/app/src/main/java/com/example/application111/frag2.java b/Application111/app/src/main/java/com/example/application111/frag2.java new file mode 100644 index 0000000..3f58022 --- /dev/null +++ b/Application111/app/src/main/java/com/example/application111/frag2.java @@ -0,0 +1,18 @@ +package com.example.application111; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.fragment.app.Fragment; + +public class frag2 extends Fragment { + //创建一个View + private View zj; + //显示布局 + public View onCreateView(final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + zj = inflater.inflate(R.layout.frag2_layout, null); + return zj; + } +} diff --git a/Application111/app/src/main/res/drawable/btn_bg_selector.xml b/Application111/app/src/main/res/drawable/btn_bg_selector.xml new file mode 100644 index 0000000..f052842 --- /dev/null +++ b/Application111/app/src/main/res/drawable/btn_bg_selector.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/Application111/app/src/main/res/drawable/ic_launcher_background.xml b/Application111/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/Application111/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Application111/app/src/main/res/drawable/ic_launcher_foreground.xml b/Application111/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/Application111/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/Application111/app/src/main/res/drawable/music0.png b/Application111/app/src/main/res/drawable/music0.png new file mode 100644 index 0000000..d23c047 Binary files /dev/null and b/Application111/app/src/main/res/drawable/music0.png differ diff --git a/Application111/app/src/main/res/drawable/music1.png b/Application111/app/src/main/res/drawable/music1.png new file mode 100644 index 0000000..01499b0 Binary files /dev/null and b/Application111/app/src/main/res/drawable/music1.png differ diff --git a/Application111/app/src/main/res/drawable/music2.png b/Application111/app/src/main/res/drawable/music2.png new file mode 100644 index 0000000..e6a3cb9 Binary files /dev/null and b/Application111/app/src/main/res/drawable/music2.png differ diff --git a/Application111/app/src/main/res/drawable/music_bg.png b/Application111/app/src/main/res/drawable/music_bg.png new file mode 100644 index 0000000..5997150 Binary files /dev/null and b/Application111/app/src/main/res/drawable/music_bg.png differ diff --git a/Application111/app/src/main/res/layout/activity_main.xml b/Application111/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..60ff916 --- /dev/null +++ b/Application111/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + diff --git a/Application111/app/src/main/res/layout/activity_music.xml b/Application111/app/src/main/res/layout/activity_music.xml new file mode 100644 index 0000000..a5dd994 --- /dev/null +++ b/Application111/app/src/main/res/layout/activity_music.xml @@ -0,0 +1,83 @@ + + + + + + + + + + +