Android开发Note--慕课网我的云音乐
这里整理了慕课网《我的云音乐(一)》项目开发中的笔记,relam目前用的不多,后面的部分咕咕咕了。
1 引入阿里nexus镜像库
maven { url 'https://maven.aliyun.com/repository/public' }
maven { url 'https://maven.aliyun.com/repository/google' }
google()
jcenter()
常用第三方库
Recycler View
CircleImageView
AndroidUtilCode
Glide
Glide-Transformations
Relam
项目开发流程
1.立项
2.原型确认
3.设计稿确认
4.代码开发
5.测试-修改
6.上线
学习项目 https://github.com/lgd8981289/IMoocMusic
开发流程
0.创建项目
1.创建MainApplication继承Application,重写其中的生命周期方法,进行全局框架的初始化和回收配置,在AndroidManifest.xml文件中配置创建的MainApplication。
2.进行一些基础样式定制
MaterialDesign风格的app需要设置如下几个三种基础颜色,
colorPrimary
colorPrimaryDark
colorAccent
从上往下看app页面,默认情况最上面的StatusBar部分的颜色是使用colorPrimaryDark
而toolBar部分则使用colorPrimary,很多app的设计是将StatusBar的颜色改为和colorPrimary一致。
使用android:statusBarColor设置StatusBar的背景色。只能在api21以上使用。可以创建values-v21文件夹,复制一份资源配置文件。
3.关闭ActionBar,直接用Theme.AppCompat.Light.NoActionBar
主题即可。
4.创建欢迎页,
app的第一个页面一般是欢迎页,欢迎页展示图片,可以根据不同情况,弹出升级弹窗、跳转到新特性引导页、跳转到登录页、跳转到主页面等多种情况。
欢迎页一般要停顿一秒,可以使用Timer来进行延时跳转。
5.自定义输入组件
分析自定义控件需要的个性化配置
第一步在attrs.xml
中定义控件属性
<declare-styleable name="inputView">
<attr name="input_hint" format="string"></attr>
<attr name="is_password" format="boolean">false</attr>
<attr name="input_icon" format="reference"></attr>
</declare-styleable>
第二步创建Java类,如果是以组合的方式实现自定义view,就让其继承FrameLayout类,并在其中构造函数中获取配置的属性。
先定义需要获取的属性变量
private int iconRes;
private String inputHint;
private boolean isPassword;
创建初始化属性配置的方法。
private void initAttrs(Context context,AttributeSet attributeSet){
if (attributeSet!=null){
TypedArray typedArray = context.obtainStyledAttributes(attributeSet, R.styleable.inputView);
iconRes = typedArray.getResourceId(R.styleable.inputView_input_icon,R.mipmap.logo);
isPassword = typedArray. getBoolean(R.styleable.inputView_is_password,false);
inputHint = typedArray.getString(R.styleable.inputView_input_hint);
typedArray.recycle();
}
}
通过context的obtainStyledAttributes方法获取AttributeSet定义的styleable。
拿到TypeArray之后可以拿到xml中定义的值。记得释放TypeArray
第三步:定义布局xml文件,使用android:background="@null"
可以隐藏输入框下划线。
在自定义view中关联我们组合的xml布局,并使自定义属性在对应控件上生效。
// 1 从xml中生成视图对象
mView = LayoutInflater.from(context).inflate(R.layout.input_view,this,false);
iconIv = mView.findViewById(R.id.iv_icon);
inputEt = mView.findViewById(R.id.et_input);
// 2 关联自定义view的设置
iconIv.setImageResource(iconRes);
inputEt.setHint(inputHint);
inputEt.setInputType(isPassword? InputType.TYPE_CLASS_TEXT|InputType.TYPE_TEXT_VARIATION_PASSWORD:InputType.TYPE_CLASS_PHONE);
// 3 向父视图中添加view 完成关联
addView(mView);
第四步:定义自定义view对外提供接口方法,这里作为输入控件,只需要返回输入内容即可。
public String getInputText(){
return inputEt.getText().toString().trim();
}
第五步:使用自定义控件
在xml中使用自定义控件,需要app属性来配置之前定义的属性。
我们封装的InputView 本质上是个Framelayout。使用View控件的时候注意要大写。
<com.example.jike.base.views.InputView
android:id="@+id/et_login_password"
android:layout_width="match_parent"
android:layout_height="@dimen/input_view_height"
android:layout_marginLeft="@dimen/common_margin"
android:layout_marginRight="@dimen/common_margin"
app:input_hint="密码"
app:input_icon="@mipmap/password"
app:is_password="true"/>
<View style="@style/divider_line"/>
6.自定义按钮样式
按钮的样式由selector + shape 构成,在selector中根据不同的状态,使用不同的shape。
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 按下或聚焦状态-->
<item android:drawable="@drawable/login_btn_background_h" android:state_focused="true" />
<item android:drawable="@drawable/login_btn_background_h" android:state_pressed="true" />
<item android:drawable="@drawable/login_btn_background_h" android:state_selected="true" />
<!-- 正常状态-->
<item android:drawable="@drawable/login_btn_background" />
</selector>
注意:不设置state时的item就是默认状态。
7.AndroidUtilCode 使用
https://github.com/Blankj/AndroidUtilCode
是开发过程中常用的工具集合。首先先引入依赖implementation 'com.blankj:utilcodex:1.29.0'
在application中进行初始化Utils.init(this);
8.Activity转场动画
Activity的转场动画通过在主题样式AppTheme
中添加windowAnimationStyle来设置动画。
<!-- 定义窗口切换动画-->
<item name="android:windowAnimationStyle">@style/ActivityAnimation</item>
具体的Activity切换的动画有四种,打开新页面时新旧两个页面的动画,关闭旧页面时新旧两个页面的动画。
<style name="ActivityAnimation" parent="android:Animation.Activity">
<!--打开Activity,新页面进入的动画-->
<item name="android:activityOpenEnterAnimation">@anim/activity_open_enter</item>
<!--打开Activity,旧页面退出的动画-->
<item name="android:activityOpenExitAnimation">@anim/activity_open_exit</item>
<!--关闭Activity,下层Activity进入的动画-->
<item name="android:activityCloseEnterAnimation">@anim/activity_close_enter</item>
<!--关闭Activity,当前Activity退出的动画-->
<item name="android:activityCloseExitAnimation">@anim/activity_close_exit</item>
</style>
具体的动画xml文件,可以使用页面的平移,缩放,旋转等动画效果。
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@integer/anim_duration">
<translate
android:fromXDelta="0%"
android:toXDelta="100%" />
</set>
设置页面缩放效果
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@integer/anim_duration">
<scale
android:fromXScale="1.0"
android:fromYScale="1.0"
android:toXScale="0.8"
android:toYScale="0.8"
android:pivotX="50%"
android:pivotY="50%"
/>
</set>
当然Activity的转场动画也可以通过代码调用,在startActivity
之后,可以调用Activity自带的overridePendingTransition
方法来实现跳转动画,第一个参数是新页面进入的动画,第二个动画是旧页面退出的动画。
Intent intent =new Intent(context, LoginActivity.class);
// 添加intent标记,可以多个标记合并在一起添加
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK|Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
((Activity)context).overridePendingTransition(R.anim.activity_open_enter,R.anim.activity_close_exit);
9.主页面专辑列表
这里使用Recycler View实现网格列表,使用的第一步是引入依赖,这里确保和appCompact引入的版本一致就行。
implementation "androidx.recyclerview:recyclerview:1.1.0"
// For control over item selection of both touch and mouse driven selection
implementation "androidx.recyclerview:recyclerview-selection:1.1.0-rc01"
第二步是设置布局管理,3列网格。
mCommendSongRv.setLayoutManager(new GridLayoutManager(this,3));
第三步是创建Adapter,这里只需要创建ViewHolder持有view,
View root = LayoutInflater.from(mContext).inflate(R.layout.item_grid_music,parent,false);
第四步解决分割线,这里使用RecyclerView.ItemDecoration来实现,ItemDecoration相当于一个画框,将itemView放入其中,画框是一个矩形,我们为其添加左边距就可以在ItemView左边添加分割线。
mCommendSongRv.addItemDecoration(new GridSpaceItemDecoration(10,mCommendSongRv));
public class GridSpaceItemDecoration extends RecyclerView.ItemDecoration {
private int mSpace;
public GridSpaceItemDecoration(int space) {
mSpace = space;
}
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
// 直接为每个Rect添加左边距
outRect.left = mSpace;
// 最左侧item不应该有分割线的
// 思路一:不为左侧item添加边距,但会导致左侧的Decoration和非左侧的装饰框大小不一致。显示内容也不一致
int position = parent.getChildLayoutPosition(view);
if (position%3==0) {
outRect.left = 0;
}
// 思路二:直接修改父容器列表的布局参数,让其整体向左偏移。
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) parent.getLayoutParams();
layoutParams.leftMargin = -mSpace;
parent.setLayoutParams(layoutParams);
}
}
10.最新音乐列表
当我们在ScrollView中使用RecyclerView时,要处理滑动冲突,让RecyclerView禁止滚动。
mCommendSongRv.setNestedScrollingEnabled(false);
当不准备让RecyclerView滚动的时候,需要动态设定RecyclerView的高度,计算方式是count*itemHeight。
通过在adapter中bindView时调用如下方法来计算并重新设置recycler高度。
private void resetRecyclerViewHeight() {
if (!rvHeight&&mRecyclerView!=null){
LinearLayout.LayoutParams rvLayoutParams = (LinearLayout.LayoutParams) mRecyclerView.getLayoutParams();
RecyclerView.LayoutParams itemLayoutParams = (RecyclerView.LayoutParams) root.getLayoutParams();
int count = getItemCount();
int itemHeight = itemLayoutParams.height;
int recyclerViewHeight = itemHeight* count;
rvLayoutParams.height = recyclerViewHeight;
mRecyclerView.setLayoutParams(rvLayoutParams);
}
}
11.展示网络图片,这里使用Glide框架搞定。引入依赖后直接使用。
Glide
.with(myFragment)
.load(url)
.centerCrop()
.placeholder(R.drawable.loading_spinner)
.into(myImageView);
12.隐藏StatusBar
getWindow().setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN
);
13.为图片添加高斯模糊
这里使用glide-transformations这个库
Glide.with(this).load("http://image3.xyzs.com/upload/d5/6f/1451265843508069/20151230/145140581662753_0.jpg")
.apply(RequestOptions.bitmapTransform(new BlurTransformation()))
.into(mBgIv);
14.实现圆形图片
这里使用CircleImageView这个库
15.自定义View动画
在res/anim目录下创建动画set
这里fillAfter 是动画结束后停在结束位置,android:interpolator 可以设定视图动画插值器。
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false"
android:fillAfter="true">
<rotate
android:duration="@integer/play_music_anim_duration"
android:fromDegrees="0"
android:interpolator="@android:anim/linear_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="infinite"
android:toDegrees="360" />
</set>
16.音乐播放
音乐可以同MediaPlayer实例来播放,可以加载线上的mp3资源。下面是设置播放资源的方式。
public void setPath(String path) {
// 第一步重置音乐播放状态
if (mMediaPlayer.isPlaying()) {
mMediaPlayer.reset();
}
// 第二步设置音乐播放路径
try {
mMediaPlayer.setDataSource(mContext, Uri.parse(path));
} catch (IOException e) {
e.printStackTrace();
}
currentPath = path;
// 第三步准备播放
mMediaPlayer.prepareAsync();
mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
if (mOnMediaPlayerHelperListener != null) {
mOnMediaPlayerHelperListener.onPreared(mp);
}
}
});
}
17.Relam数据库
————————————————————————————————————————————
问题:
1.如果使用AndroidStudio的热重载,app在手机上没有卸载干净会出现如下问题
Error while executing: am start -n "com.example.jike/com.example.jike.welcome.WelcomeActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -D
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.example.jike/.welcome.WelcomeActivity }
Error type 3
Error: Activity class {com.example.jike/com.example.jike.welcome.WelcomeActivity} does not exist.
Error while Launching activity
解决办法也很简单,直接卸载即可。
adb uninstall com.example.jike
- app:srcCompat="@mipmap/back" 是可以设置低版本的系统上展示的图片
本文地址:https://blog.csdn.net/d0d0bird/article/details/107146562