1. 說明
基于我們前邊寫的自定義View的入門谷市、自定義TextView迫悠、仿QQ運(yùn)動(dòng)步數(shù)创泄、玩轉(zhuǎn)字體變色、類似QQ運(yùn)動(dòng)步數(shù)進(jìn)度條搁拙、3個(gè)形狀來回切換等自定義View外,這節(jié)課我們來寫一個(gè)評(píng)分控件盐茎,類似于淘寶滑进、騰訊課堂的評(píng)分控件 —— RatingBar。
我們前邊寫的上邊的6個(gè)效果数冬,都屬于靜態(tài)自定義View控件拐纱,不涉及用戶去觸摸該控件揍庄;
但是沃测,從這節(jié)課開始蒂破,我們所寫的效果就屬于交互控件,何為交互控件哎媚? 就是用戶可以在這個(gè)控件上邊去觸摸拨与,即就是按下、移動(dòng)箩朴、抬起炸庞。
2. 效果圖如下:
剛開始進(jìn)入app是一個(gè)圖片都沒有選中,如圖所示:
手指觸摸后,就選中觸摸的圖片胁孙,如圖所示:
3. 思路分析
3.1:首先剛進(jìn)來是初始化的樣子:
1>:就是只有5張未被選中的圖片,這里需要自定義屬性狂票、2張圖片資源慌盯、評(píng)分的等級(jí)數(shù)量;
2>:指定控件的寬高孕讳;
3>:繪制圖片Bitmap;
3.2:需要在onTouch()方法中處理用戶和該控件的交互事件璃饱;
3.3:優(yōu)化部分:
1>:如果分?jǐn)?shù)相同,就不需要再去繪制了谒撼;
2>:就是盡量減少onDraw()方法的調(diào)用;
3>:onTouchEvent()方法必須return true辩蛋,如果返回false,就表示事件不消費(fèi)咒循,第一次可以進(jìn)入MotionEvent.ACTION_MOVE事件,以后就不能進(jìn)來了蚁署,所以這里必須return true光戈,并且以后只要是自定義View,并且凡是涉及到用戶觸摸事件跷睦,onTouchEvent()方法都return true;
4. 代碼如下
自定義RatingBar.java代碼如下:
/**
* Email: 2185134304@qq.com
* Created by JackChen 2018/3/19 15:01
* Version 1.0
* Params:
* Description: 評(píng)分控件RatingBar
*/
public class RatingBar extends View {
// 用BitmapFactory 解析出來的沒有選中的圖片
private Bitmap mStarNormalBitmap;
// 用BitmapFactory 解析出來的已經(jīng)選中的圖片
private Bitmap mStarFocusBitmap;
// 總共的分?jǐn)?shù)
private int mGradeNumber = 5 ;
// 當(dāng)前的分?jǐn)?shù)
private int mCurrentNumber = 0 ;
public RatingBar(Context context) {
this(context,null);
}
public RatingBar(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public RatingBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// 以下是獲取自定義屬性
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.RatingBar);
// 獲取資源id
int starNormalId = array.getResourceId(R.styleable.RatingBar_starNormal, 0);
if (starNormalId == 0){
throw new RuntimeException("請(qǐng)?jiān)O(shè)置屬性 starNormal") ;
}
// 用BitmapFactory 解析資源
mStarNormalBitmap = BitmapFactory.decodeResource(getResources(), starNormalId);
int starFocusId = array.getResourceId(R.styleable.RatingBar_starFocus, 0);
if (starFocusId == 0){
throw new RuntimeException("請(qǐng)?jiān)O(shè)置屬性 starFocus") ;
}
// 用BitmapFactory 解析資源
mStarFocusBitmap = BitmapFactory.decodeResource(getResources(), starFocusId);
// 獲取分?jǐn)?shù)
mGradeNumber = array.getInt(R.styleable.RatingBar_gradeNumber , mGradeNumber) ;
array.recycle();
}
/**
* 測(cè)量控件的寬高
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 控件的寬度
int width = mStarFocusBitmap.getWidth() * mGradeNumber;
int height = mStarFocusBitmap.getHeight();
setMeasuredDimension(width , height);
}
/**
* 畫選中的圖片和未選中的圖片
* @param canvas
*/
@Override
protected void onDraw(Canvas canvas) {
// super.onDraw(canvas);
for (int i = 0; i < mGradeNumber; i++) {
// i * 星星的寬度
int x = i * mStarFocusBitmap.getWidth() ;
// 觸摸的時(shí)候 mCurrentGrade的值是不斷變化的
if (mCurrentNumber > i){
canvas.drawBitmap(mStarFocusBitmap , x , 0 , null);
}else{
canvas.drawBitmap(mStarNormalBitmap , x , 0 , null);
}
}
}
/**
* 處理用戶觸摸
* 移動(dòng)层玲、按下辛块、抬起,處理邏輯都是一樣的,判斷手指的位置,根據(jù)當(dāng)前位置計(jì)算出分?jǐn)?shù)莱衩,然后去刷新并顯示
* @param event
* @return
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
// case MotionEvent.ACTION_DOWN: // 盡量減少onDraw()方法的調(diào)用
case MotionEvent.ACTION_MOVE:
// case MotionEvent.ACTION_UP: // 盡量減少onDraw()
// 手指的位置睹晒,計(jì)算當(dāng)前的分?jǐn)?shù)
float moveX = event.getX();
// 計(jì)算當(dāng)前分?jǐn)?shù)
int currentGrade = (int) (moveX/mStarFocusBitmap.getWidth()+1);
//范圍問題
if (currentGrade < 0){
currentGrade = 0 ;
}
if (currentGrade > mGradeNumber){
currentGrade = mGradeNumber ;
}
// 分?jǐn)?shù)相同的情況下伪很,就不要再去繪制了,盡量減少onDraw()方法的調(diào)用
if (currentGrade == mGradeNumber){
return true ;
}
// 判斷完異常情況之后呆盖,最后記錄當(dāng)前分?jǐn)?shù),然后重新繪制
mCurrentNumber = currentGrade ;
invalidate(); // 因?yàn)?invalidate()方法會(huì)調(diào)用
break;
}
return true;
}
}
attrs.xml資源文件如下:
<?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"/>
</declare-styleable>
</resources>
直接在activity_main.xml布局文件中引用該RatingBar控件即可:
<?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="com.jackchen.view_day06_2.MainActivity">
<com.jackchen.view_day06_2.RatingBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:starNormal="@drawable/star_normal"
app:starFocus="@drawable/star_selected"
app:gradeNumber="5"
/>
</RelativeLayout>
具體代碼已上傳至github:
https://github.com/shuai999/View_day06_2.git