先看看效果图
1.自定义 View 的基本流程
- 创建 View Class
- 创建 attr 属性文件,确定属性
- View Class 绑定 attr 属性
- onMeasure 测量
- onDraw 绘制
- onTouchEvent ( 用户交互需要处理 )
1.1 创建 View Class
package com.example.view_day05_ratingbar;import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;import androidx.annotation.Nullable;public class RatingBar extends View {private static final String TAG = "wqq";private Bitmap mStartNormalBitmap,mStartFocusBitmap;private int mGradeNumber = 5;private int mCurrentGrade = 0;public RatingBar(Context context) {this(context, null);}public RatingBar(Context context, @Nullable @org.jetbrains.annotations.Nullable AttributeSet attrs) {this(context, attrs, 0);}public RatingBar(Context context, @Nullable @org.jetbrains.annotations.Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);TypedArray array = context.obtainStyledAttributes(attrs,R.styleable.RatingBar);int starNormalID = array.getResourceId(R.styleable.RatingBar_starNormal,0);if (starNormalID == 0) {throw new RuntimeException("请设置属性 startNormal");}mStartNormalBitmap = BitmapFactory.decodeResource(getResources(),starNormalID);int startFocusId = array.getResourceId(R.styleable.RatingBar_starFocus, 0);if (startFocusId == 0) {throw new RuntimeException("请设置属性 startFocusId");}mStartFocusBitmap = BitmapFactory.decodeResource(getResources(), startFocusId);mGradeNumber = array.getInt(R.styleable.RatingBar_gradeNumber,0);array.recycle();}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);//高度 一张图片的高度,自己去实现 paddingint height = mStartNormalBitmap.getHeight();int width = mGradeNumber * mStartFocusBitmap.getWidth();setMeasuredDimension(width,height);}@Overrideprotected void onDraw(Canvas canvas) {for (int i = 0; i < mGradeNumber; i++) {int x = i * mStartFocusBitmap.getWidth();if (mCurrentGrade > i) {canvas.drawBitmap(mStartFocusBitmap,x,0,null);} else {canvas.drawBitmap(mStartNormalBitmap,x,0,null);}}}@Overridepublic boolean onTouchEvent(MotionEvent event) {//移动 按下 抬起 处理逻辑都是一样,判断手指的位置,根据当前位置计算出分数,再去刷新界面显示switch(event.getAction()) {case MotionEvent.ACTION_DOWN: //按下case MotionEvent.ACTION_MOVE: // 移动//case MotionEvent.ACTION_UP: // 抬起,up事件可以去掉,优化ondraw调用次数float moveX = event.getX(); // event.getX(),获取相对于当前控件的位置;event.getRawX(),获取屏幕的x位置Log.e(TAG, "movex: "+ moveX + "event:"+ event.getAction());int currentGrade = (int)(moveX/mStartFocusBitmap.getWidth() + 1);if (currentGrade < 0) {currentGrade = 0;}if (currentGrade > mGradeNumber) {currentGrade = mGradeNumber;}//相等的话,就不用重复绘制了if (currentGrade == mCurrentGrade) {return true;}mCurrentGrade = currentGrade;//刷新显示invalidate();// 由于ondraw流程很长,尽量减少ondraw的调用,减少内存break;}return true;}
}
下篇文字会分析 onTouchEvent 源码,为什么要设置返回 true
1.2 创建 attr
<?xml version="1.0" encoding="utf-8"?>
<resources><declare-styleable name="RatingBar"><!--未选中引用--><attr name="starNormal" format="reference" /><!--选中引用--><attr name="starFocus" format="reference" /><!--最大的分数--><attr name="gradeNumber" format="integer" /><!--当前的分数--><attr name="currentGrade" format="integer" /><!--星星之间的间距--><attr name="starPadding" format="dimension" /></declare-styleable>
</resources>
1.3 创建图片资源
在\app\src\main\res目录下创建文件夹drawable-xhdpi,将星星图片复制进去。
1.4 布局文件
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Hello World!"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent" /><com.example.view_day05_ratingbar.RatingBarandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:background="@color/purple_200"app:starPadding="50dp"app:starFocus="@drawable/select"app:starNormal="@drawable/start_normal"app:gradeNumber="5"/></RelativeLayout>