Android开发Note--慕课网我的云音乐

2022-07-29,,,,

这里整理了慕课网《我的云音乐(一)》项目开发中的笔记,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
  1. app:srcCompat="@mipmap/back" 是可以设置低版本的系统上展示的图片

本文地址:https://blog.csdn.net/d0d0bird/article/details/107146562

《Android开发Note--慕课网我的云音乐.doc》

下载本文的Word格式文档,以方便收藏与打印。