Android插件化开发指南——实践之仿酷狗音乐首页(自定义ImageView控件)

1. 前言

拟定实现效果部分为下图的歌单列表部分,也就是图中红线框出来的部分。为了方便这里使用RecyclerView来进行实现,对于图中所需要的正方形图片显示控件,这里就考虑使用自定义的ImageView
在这里插入图片描述

2. 基础环境——实现RecyclerView的网格布局

首先在xml文件中定义RecyclerView

<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/fx_music_song_list_below_time"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingLeft="8dp"
    android:paddingRight="8dp"
    />

然后定义每个item的布局显示样式的布局文件,这里我定义为fx_music_song_list_below_time_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    >
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        >
        <!-- 自定义ImageView-->
        <com.weizu.mymusicdemo.customcomponents.RoundEqualWidthImageView
            android:id="@+id/fx_music_song_list_below_time_item_img"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:src="@drawable/fx_music_rmdt_play"
            android:background="@drawable/fx_music_round_bg"
            android:scaleType="centerCrop"
            app:radius="8dp"
            />
            
        <LinearLayout
            android:layout_width="200dp"
            android:layout_height="25dp"
            android:gravity="center"
            android:orientation="horizontal"
            android:layout_alignBottom="@id/fx_music_song_list_below_time_item_img"
            >
            <ImageView
                android:layout_width="0dp"
                android:layout_height="20dp"
                android:layout_weight="1"
                android:src="@drawable/fx_music_song_list_below_time_item_play"
                />
            <TextView
                android:id="@+id/fx_music_song_list_below_time_item_play_number"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="2"
                android:text="123万"
                android:textColor="@color/white"
                android:gravity="left|center_vertical"
                android:textSize="12sp"
                />
        </LinearLayout>
    </RelativeLayout>

    <TextView
        android:id="@+id/fx_music_song_list_below_time_item_intro"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="123万"
        android:textSize="12sp"
        android:textColor="@color/black"
        />

</LinearLayout>

最后进行定义一个适配器,用来加载数据到具体的item项:

public class FxPageMusicSongListBelowTimeRecycleViewAdapter<T extends FxPageSongListItemBean> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    
    

    private Context mContext;
    private int mRescourseId;
    private List<T> mData;

    public FxPageMusicSongListBelowTimeRecycleViewAdapter(Context context, int rescourseId, List<T> data) {
    
    
        this.mContext = context;
        this.mRescourseId = rescourseId;
        this.mData = data;
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    
    
        // 实例化对应的子项xml文件
        View rootView = LayoutInflater.from(mContext).inflate(mRescourseId, parent, false);
        return new MyViewHolder(rootView);
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
    
    
        MyViewHolder myViewHolder = (MyViewHolder) holder;
        // 设置数据到每个子项
        FxPageSongListItemBean bean = (FxPageSongListItemBean) mData.get(position);
        Glide.with(mContext).load(bean.getCoverImageUrl()).into(myViewHolder.coverImageView);
        myViewHolder.playNumber.setText(bean.getPlayNumber());
        myViewHolder.introduce.setText(bean.getIntroduceInfo());
    }

    @Override
    public int getItemCount() {
    
    
        return mData.size();
    }

    static class MyViewHolder extends RecyclerView.ViewHolder{
    
    

        private ImageView coverImageView;
        private TextView playNumber;
        private TextView introduce;

        public MyViewHolder(@NonNull View itemView) {
    
    
            super(itemView);
            coverImageView = itemView.findViewById(R.id.fx_music_song_list_below_time_item_img);
            playNumber = itemView.findViewById(R.id.fx_music_song_list_below_time_item_play_number);
            introduce = itemView.findViewById(R.id.fx_music_song_list_below_time_item_intro);
        }
    }
}

当然最后一步就是使用了,就是实例化适配器对象,得到RecyclerView实例。然后设置网格布局管理器,并简单设置一下间距:

FxPageMusicSongListBelowTimeRecycleViewAdapter<FxPageSongListItemBean> adapter =
        new FxPageMusicSongListBelowTimeRecycleViewAdapter<>(getContext(), R.layout.fx_music_song_list_below_time_item, beans);

GridLayoutManager manager = new GridLayoutManager(getContext(), 3);
recyclerView.setLayoutManager(manager);
// 设置Item的间距
recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
    
    
    @Override
    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
    
    
        super.getItemOffsets(outRect, view, parent, state);
        outRect.left = 8;
        outRect.right = 8;
        outRect.top = 16;
        outRect.bottom = 16;
    }
});
recyclerView.setAdapter(adapter);

效果:
在这里插入图片描述
但是很容易发现一点就是,每个图片的底部的显示的人数部分这里看不清。其实观看原图可以发现在酷狗音乐中其实是使用了一个暗色的背景在底部。所以这里我将在自定义ImageView的时候,加上一层暗色的背景渐变。那么,接下来就进入本篇博客的正题,即:自定义ImageView控件。

3. 自定义ImageView

其实在前面的xml布局文件中可以看见,这里自定义的ImageViewcom.weizu.mymusicdemo.customcomponents.RoundEqualWidthImageView类,且圆角半径radius为自定义属性。

  • 圆角可以使用canvas的裁剪工具来实现;
  • 遮罩层的暗色渐变可以使用在其上再绘制一张图片;
  • 在测量的时候设置宽度一样,就可以得到正方形;
  • 设置缩放类型,使它中心裁剪,填充满;

那么可以定义为:

public class RoundEqualWidthImageView extends AppCompatImageView {
    
    
    private int width;
    private int height;
    private int roundDp;

    public RoundEqualWidthImageView(Context context) {
    
    
        super(context);
    }

    public RoundEqualWidthImageView(Context context, @Nullable AttributeSet attrs) {
    
    
        super(context, attrs);
        initData(context, attrs);
    }

    private void initData(Context context, AttributeSet attrs) {
    
    
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.roundEqualWidthImageView);
        // 默认为直角,没有弧度
        roundDp = array.getDimensionPixelOffset(R.styleable.roundEqualWidthImageView_radius, 0);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    
    
        super.onLayout(changed, left, top, right, bottom);
        width = getWidth();
        height = getHeight();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    
    
        super.onMeasure(widthMeasureSpec, widthMeasureSpec);  // 设置宽度一样
    }

    @Override
    protected void onDraw(Canvas canvas) {
    
    
        // 设置缩放类型
        setScaleType(ScaleType.CENTER_CROP);
        if (width < roundDp || height < roundDp) roundDp = 5;
        Path path = new Path();
        // 四个圆角裁剪
        path.moveTo(roundDp, 0);
        path.lineTo(width - roundDp, 0);
        path.quadTo(width, 0, width, roundDp);
        path.lineTo(width, height - roundDp);
        path.quadTo(width, height, width - roundDp, height);
        path.lineTo(roundDp, height);
        path.quadTo(0, height, 0, height - roundDp);
        path.lineTo(0, roundDp);
        path.quadTo(0, 0, roundDp, 0);
        canvas.clipPath(path);
        super.onDraw(canvas);
        // 绘制暗色渐变
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.fx_music_song_list_below_time_item_cover_bg, null);
        canvas.drawBitmap(bitmap, 0, 0, null);
    }
}

对于另一个圆角半径属性,这里新建一个values/attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="roundEqualWidthImageView">
        <attr name="radius" format="dimension"/>
    </declare-styleable>

</resources>

那么在使用的时候,就简单指定基本属性和圆角半径即可:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    >
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        >
        <com.weizu.mymusicdemo.customcomponents.RoundEqualWidthImageView
            android:id="@+id/fx_music_song_list_below_time_item_img"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:src="@drawable/fx_music_rmdt_play"
            app:radius="8dp"
            />
            ...
    </RelativeLayout>
</LinearLayout>

现在效果:
在这里插入图片描述
同样的为了记录,这部分代码我上传到了githubmymusicdemo-03

3. 后记

在加载图片的时候使用的Glide的加载 ,突然想起了之前自己写的部分单/多线程断点下载的简单封装,感觉空了可以研究下如何继续深入下去。


References

Guess you like

Origin blog.csdn.net/qq_26460841/article/details/121262724