音乐应用开发经验笔记
1、不同界面间的跳转动画
Intent intent = new Intent(this, cla);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
//使用这个方法设置跳转动画
overridePendingTransition(R.anim.zoomin, R.anim.zoomout);
finish();
2、《隐私政策》字体颜色和点击事件实现
字体颜色:首先是区分《隐私协议》和《用户手册》
//一大帕拉 String 内容
String privacyPolicyString = getResources().getString(R.string.privacy_policy_text);
//隐私协议
String privacyPolicy = getResources().getString(R.string.privacy_policy);
//用户协议
String userPolicy = getResources().getString(R.string.user_policy);
//字符下标
int indexPrivacy = privacyPolicyString.indexOf(privacyPolicy);
int indexUser = privacyPolicyString.indexOf(userPolicy);
//需要显示的字符串
SpannableString spannableString = new SpannableString(privacyPolicyString);
//设置点击字体颜色,必须声明两个对象,否则只会出现一个变色
ForegroundColorSpan colorSpan = new ForegroundColorSpan(getResources().getColor(R.color.colorTheme2));
ForegroundColorSpan colorSpan2 = new ForegroundColorSpan(getResources().getColor(R.color.colorTheme2));
spannableString.setSpan(colorSpan, indexPrivacy, indexPrivacy + privacyPolicy.length(), Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
spannableString.setSpan(colorSpan2, indexUser, indexUser + userPolicy.length(), Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
privacyText.setText(spannableString);
//下面这句必须使用使用,否则点击事件无效
privacyText.setMovementMethod(LinkMovementMethod.getInstance());
span点击事件学习参考参考连接
3、MVVM 中的VM(ViewModel使用总结)
保持VM是单例
//饿汉式 begin
public MusicModel(){}
public static MusicModel getInstance(){
return MusicModelHoldr.instance;
}
private static class MusicModelHoldr{
private static final MusicModel instance = new MusicModel();
}
//饿汉式 end
使用:(1)ViewModel中监听事件
private MutableLiveData<String> stringName= new MutableLiveData<>();
private LiveData<String> name= Transformations.map(stringName,
new Function<String, String>() {
@Override
public String apply(String input) {
return input;
}
});
//使用get set获取对象
(2)在使用的地方监听,并改变UI
musicModel.getName().observe(this, new Observer<Boolean>() {
@Override
public void onChanged(String input) {
}
});
4、应用导航栏使用
(1).先导入Navigation需要的框架,然后在需要的xml文件最下方添加组件信息,app:menu 是导航栏必须使用的属性,否则导航栏是空的。
NavigationView 需要包含在 DrawerLayout 之中。
<!--headerLayout:插入导航头部布局 -->
<!--menu :导航界面具体项 -->
<!--layout_gravity:左边展开还是右边展开 -->
<com.google.android.material.navigation.NavigationView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:id="@+id/nav_controller_view_tag"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:menu="@menu/activity_main_drawer"/>
</androidx.drawerlayout.widget.DrawerLayout>
(2)。DrawerLayout打开和关闭的方法
//左边打开
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()){
case android.R.id.home://手机系统导航栏ID
drawerLayout.openDrawer(GravityCompat.START);
break;
}
return super.onOptionsItemSelected(item);
}
drawerLayout.closeDrawer(GravityCompat.START);//左边关闭
(3).设置 navigationView head_view 点击事件(在java中使用了navigationView.inflateHeaderView()方法加载头部布局,就不要在xml中再添加headLayout设置布局,否则会重复加载。
view = navigationView.inflateHeaderView(R.layout.nav_header_main);//head布局
imageHead = view.findViewById(R.id.image_head);//布局文件中的ImageView
signature = view.findViewById(R.id.textView);//布局文件中的TextView
(4).NavigationView的点击事件
//drawLayout 导航栏部分点击图标
navigationView.setNavigationItemSelectedListener(this);
//navigation的点击事件
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item){}
5、Pop弹窗
initPopView();//初始化 popView 数据
//popView,需要弹出的组件,1080:弹出的高度
PopupWindow popupWindow = new PopupWindow(popView,
ViewGroup.LayoutParams.MATCH_PARENT,
1080,
true);
popupWindow.setAnimationStyle(R.style.popwin_anim_style);
popupWindow.setOutsideTouchable(true);//触摸屏幕外是否会关闭
popupWindow.setTouchable(true);
//动画完毕后底部的位置
popupWindow.showAtLocation(viewContent,Gravity.BOTTOM,0,408);
/**
* 初始化 popView 数据
* @省略大部分代码,节约版面
*/
private void initPopView(){
adapter = new MainPopListAdapter(MainActivity.this,audioInfoTables,MainPopListAdapter.NOMOREDATA);
GridLayoutManager manager = new GridLayoutManager(MainActivity.this,1);
curplaylist.setLayoutManager(manager);
curplaylist.setAdapter(adapter);
}
6、注册广播
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BroadCastReceiveConstant.BROADCAST_SEND_AUDIO_INFO);
MusicInfoReceive receiver = new MusicInfoReceive();
MainActivity.this.registerReceiver(receiver,intentFilter);
7、返回键处理+时间判断
/**
* 按返回键
*/
@Override
public void onBackPressed() {
drawerLayout.closeDrawer(GravityCompat.START);
if ((System.currentTimeMillis() - mExitTime) > 2000) {
ToastShowUtil.showTextToast(getApplicationContext(), getString(R.string.back_tip));
mExitTime = System.currentTimeMillis();
}
}
8、动态权限请求
/**
* 动态权限请求
*/
private void permissionsRequest() {
PermissionX.init(this).permissions(
//写入文件
Manifest.permission.WRITE_EXTERNAL_STORAGE)
.onExplainRequestReason(new ExplainReasonCallbackWithBeforeParam() {
@Override
public void onExplainReason(ExplainScope scope, List<String> deniedList, boolean beforeRequest) {
scope.showRequestReasonDialog(deniedList, "即将申请的权限是程序必须依赖的权限", "我已明白");
}
})
.onForwardToSettings(new ForwardToSettingsCallback() {
@Override
public void onForwardToSettings(ForwardScope scope, List<String> deniedList) {
scope.showForwardToSettingsDialog(deniedList, "您需要去应用程序设置当中手动开启权限", "我已明白");
}
})
.setDialogTintColor(R.color.bottom_navigation_background_color, R.color.common_menu_background_color)
.request(new RequestCallback() {
@Override
public void onResult(boolean allGranted, List<String> grantedList, List<String> deniedList) {
if (allGranted) {
//通过后的业务逻辑
} else {
ToastShowUtil.showTextToast(getApplicationContext(),"您拒绝了如下权限:" + deniedList);
}
}
});
}
9、DBFlow框架使用
学习链接:https://blog.csdn.net/jzman/article/details/80012837
10、从后台下载文件
this.mContext = context;
Request request = new Request.Builder()
.url(url)
.build();
client.newCall(request).enqueue(new Callback() {
//下载失败
@Override
public void onFailure(Call call, IOException e) {
download.error(e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
download.start(audioInfo);
InputStream stream = null;
byte[] bytes = new byte[2048];
int len = 0;
FileOutputStream fileOutputStream = null;
String savePath = isExistDir(filePath);
try{
stream = response.body().byteStream();
long total = response.body().contentLength();
File file = new File(savePath,fileName+".mp3");
fileOutputStream = new FileOutputStream(file);
long sum = 0;
while ((len = stream.read(bytes))!= -1){
fileOutputStream.write(bytes,0,len);
sum += len;
//下载进入
int progress = (int) (sum * 1.0f / total * 100);
download.progress(progress);
}
fileOutputStream.flush();
//下载完成可在下面定制操作
download.finish(audioInfo,filePath + File.separator + fileName + ".mp3");
} catch (Exception e) {
e.printStackTrace();
}
finally {
try {
if (stream!= null){
stream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(fileOutputStream != null){
fileOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
});
判断文件是否存在
/**
* 判断是否存在该文件夹
* @param saveDir
* @return
*/
private String isExistDir(String saveDir) throws IOException {
File downloadFile = new File(saveDir);
if (!downloadFile.mkdirs()) {
downloadFile.createNewFile();
}
String savePath = downloadFile.getAbsolutePath();
return savePath;
}
11、在AS中使用 网页
webView = root.findViewById(R.id.web_view);
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
webView.getSettings().setLoadWithOverviewMode(true);
webView.loadUrl("file:///android_asset/manual.html");
12、酷狗音乐API
https://blog.csdn.net/qq_32551929/article/details/87256150
http://m.mamicode.com/info-detail-2817603.html
更多自己另找
13、select使用
也可以用图片
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/skin_kg_ic_playing_bar_playlist_queue_pressed" android:state_pressed="true" />
<item android:drawable="@drawable/skin_kg_ic_playing_bar_playlist_queue_pressed" android:state_focused="true" />
<item android:drawable="@drawable/skin_kg_ic_playing_bar_playlist_queue_default" />
</selector>
14、NavHostFragment使用
xml用使用fragment
<!-- 一般情况下使用 FrameLayout 设置碎片,这次模仿 Google 使用 fragment 添加碎片-->
<!-- defaultNavHost 和 navGraph 属性来自于 navigation 框架 -->
<!-- defaultNavHost : 是否设置 navigation 返回栈 -->
<!-- navGraph : 绑定 navigation 文件 ( 需要创建 navigation 文件夹 )-->
<!-- 一定要配置 name 属性指定首发启动的fragment,否则会报 “ Error inflating class fragment ”-->
<!-- 登陆 / 注册界面 -->
<fragment
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/nav_host_login_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
app:defaultNavHost="false"
app:navGraph="@navigation/navigation_login"/>
创建navigation文件夹
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/navigation_fragment"
app:startDestination="@id/fragment_search">
<fragment android:id="@+id/fragment_search"
android:name="com.example.music.fragment.SearchFragment"
android:label="@string/bottom_navigation_search"
tools:layout="@layout/fragment_search"/>
<fragment android:id="@+id/fragment_my"
android:name="com.example.music.fragment.MyFragment"
android:label="@string/bottom_navigation_my"
tools:layout="@layout/fragment_my"/>
</navigation>
在java中使用
controller = Navigation.findNavController(this,R.id.fragment);
NavigationUI.setupWithNavController(bottomNavigationView,controller);
AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(
R.id.fragment_search,R.id.fragment_my
).build();
//关联底部导航栏
NavigationUI.setupActionBarWithNavController(this,controller,appBarConfiguration);
15、刷新框架
<com.github.ybq.android.spinkit.SpinKitView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:padding="20dp"
android:id="@+id/loading_img"
style="@style/SpinKitView"
app:SpinKit_Color="#FF0000"/>
16、Navigation一般使用
NavigationView navigationView = popView.findViewById(R.id.item_list);
navigationView.setNavigationItemSelectedListener(RankSongAdapter.this);
17、底部导航栏使用
<!-- 修改icon 和 title 可以通过创建 color 文件夹实现 -->
<!-- bottom navigation 背景默认主题为深色,设置背景颜色时不能呢使用透明度,否则会出现重影-->
<com.google.android.material.bottomnavigation.BottomNavigationView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:id="@+id/navigation_view"
android:background="@color/bottom_navigation_background_color"
app:menu="@menu/bottom_navigation_menu"
app:itemIconTint="@color/bottom_navigation_icon_select"
app:itemTextColor="@color/bottom_navigation_title_select"/>
itemIconTint 和 itemTextColor更换颜色
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!--实现自定义 bottom navigation 图片和标题颜色-->
<item android:color="@color/bottom_navigation_select_icon_color" android:state_selected="true"/>
<item android:color="@color/bottom_navigation_unSelect_icon_color" android:state_selected="false"/>
</selector>
18、常用框架
//导入ButterKnife框架
implementation 'com.jakewharton:butterknife:8.8.1'
//导入material 模式
implementation 'com.google.android.material:material:1.4.0'
//导入cardView
implementation 'androidx.cardview:cardview:1.0.0'
//导入底部导航 navigation 框架,实现碎片管理
implementation 'androidx.navigation:navigation-fragment:2.0.0'
implementation 'androidx.navigation:navigation-ui:2.0.0'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
//导入recycleView 框架
//noinspection GradleCompatible
implementation 'com.android.support:recyclerview-v7:30.0.0'
//导入 jsoup 框架
implementation 'org.jsoup:jsoup:1.13.1'
//Loading加载动画
implementation 'com.github.ybq:Android-SpinKit:1.2.0'
//下拉刷新框架
implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0'
//banner轮播框架
implementation 'com.youth.banner:banner:1.4.9'
//图片缓存框架,提供流畅图片加载
implementation 'com.github.bumptech.glide:glide:4.5.0'
//导入okhttp框架
implementation 'com.squareup.okhttp3:okhttp:4.7.2'
implementation 'com.squareup.okio:okio:1.5.0'
//导入DBFlow数据库框架
annotationProcessor "com.github.Raizlabs.DBFlow:dbflow-processor:4.2.4"
implementation "com.github.Raizlabs.DBFlow:dbflow-core:4.2.4"
implementation "com.github.Raizlabs.DBFlow:dbflow:4.2.4"
//fastjson
implementation 'com.alibaba:fastjson:1.1.54.android'
//权限请求框架
implementation 'com.permissionx.guolindev:permissionx:1.4.0'
//greendao
implementation 'org.greenrobot:greendao:3.3.0'
implementation 'org.greenrobot:greendao-generator:3.3.0'
//跨线程/进程框架
implementation 'org.greenrobot:eventbus:3.1.1'