Android自定義控制元件(二)-給自定義控制元件新增事件
阿新 • • 發佈:2019-01-22
在這篇部落格中主要講解給Android自定義控制元件新增點選事件,實現可以按住百分比圓圈在螢幕上進行拖動圓圈的功能。分兩部分講,第一部分是獲取自定義控制元件的座標,第二部分是重新繪製控制元件。
第一部分:獲取自定義控制元件座標
首先看一張圖,這是自定義控制元件中獲取座標的函式,各函式獲取的座標含義如下所示:
1.view獲取自身座標:getLeft(),getTop(),getRight(),getBottom()
getTop:獲取到的,是view自身的頂邊到其父佈局頂邊的距離
getLeft:獲取到的,是view自身的左邊到其父佈局左邊的距離
getRight:獲取到的,是view自身的右邊到其父佈局左邊的距離
getBottom:獲取到的,是view自身的底邊到其父佈局頂邊的距離
2.view獲取自身寬高:getHeight(),getWidth()
3.motionEvent獲取座標:getX(),getY(),getRawX(),getRawY()
getX():獲取點選事件相對控制元件左邊的x軸座標,即點選事件距離控制元件左邊的距離
getY():獲取點選事件相對控制元件頂邊的y軸座標,即點選事件距離控制元件頂邊的距離
getRawX():獲取點選事件相對整個螢幕左邊的x軸座標,即點選事件距離整個螢幕左邊的距離
getRawY():獲取點選事件相對整個螢幕頂邊的y軸座標,即點選事件距離整個螢幕頂邊的距離
第二部分:計算控制元件左上角座標,當按下控制元件移動的時候,計算偏移距離,重新繪製,程式碼及效果圖如下所示:
import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.RectF; import android.support.annotation.Nullable; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; public class SimpleView extends View { private final static String TAG = SimpleView.class.getSimpleName(); //畫筆 private Paint mPaint; private RectF oval; //事件處理 private EventHandle mEventHandle; //滑鼠按下位置 private int startX,startY; //按下滑鼠時控制元件的位置 private int startLeft,startTop; //狀態列高度 int statusHeight = 0; public SimpleView(Context context) { super(context); init(); } public SimpleView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } public SimpleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init(){ mPaint = new Paint(); //設定是否使用抗鋸齒功能,會消耗較大資源,繪製圖形速度會變慢。 mPaint.setAntiAlias(true); mPaint.setTextSize(30.0f); oval=new RectF(); mEventHandle=null; startY=startX=0; int resourceId = this.getResources().getIdentifier("status_bar_height", "dimen", "android"); if (resourceId > 0) { statusHeight = this.getResources().getDimensionPixelSize(resourceId); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); Log.e(TAG, "onMeasure--widthMode-->" + widthMode); switch (widthMode) { case MeasureSpec.EXACTLY: //精確值模式,當控制元件的layout_width和layout_height屬性指定為具體數值或match_parent時。 break; case MeasureSpec.AT_MOST: //最大值模式,當空間的寬高設定為wrap_content時。 break; case MeasureSpec.UNSPECIFIED: //未指定模式,View想多大就多大,通常在繪製自定義View時才會用。 break; } //取最小邊為控制元件的寬高的最小值 int minWidth=widthSize>heightSize?heightSize:widthSize; setMeasuredDimension(minWidth,minWidth); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mPaint.setColor(Color.GRAY); // FILL填充, STROKE描邊,FILL_AND_STROKE填充和描邊 mPaint.setStyle(Paint.Style.FILL_AND_STROKE); int with = getWidth(); int height = getHeight(); Log.e(TAG, "onDraw---->" + with + "*" + height); float radius = with / 2-5; canvas.drawCircle(with / 2, with / 2, radius, mPaint); mPaint.setColor(Color.RED); oval.set(with / 2 - radius, with / 2 - radius, with / 2 + radius, with / 2 + radius);//用於定義的圓弧的形狀和大小的界限 int sweepAngle=120; canvas.drawArc(oval, 0, -sweepAngle, true, mPaint); //根據進度畫圓弧 double percent=sweepAngle/360.0; //設定文字顏色 mPaint.setColor(Color.WHITE); //繪製文字百分比資料 canvas.drawText(String.format("%.2f",percent)+"%",(float)(with/2+radius*Math.cos(sweepAngle*Math.PI/360)/4) ,(float)(with/2-radius*Math.sin(sweepAngle*Math.PI/360)/3),mPaint); canvas.drawText(String.format("%.2f",1-percent)+"%",(float)(with/2-radius*Math.cos(sweepAngle*Math.PI/360)) ,(float)(with/2+radius*Math.sin(sweepAngle*Math.PI/360)/3),mPaint); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: startX=(int)event.getRawX(); startY=(int)event.getRawY(); startLeft=(int)(startX-event.getX()); /** * 這裡startTop計算有些偏離,原因在於計算時加入了標題欄和狀態列的高度 * 注意:要是你的Activity沒有去掉標題欄,這裡還要去掉標題欄的高度 */ startTop= (int)(startY-event.getY())-statusHeight;//減去狀態列高度 break; case MotionEvent.ACTION_MOVE: if(mEventHandle!=null) { mEventHandle.onTouchEvent(event); }else{ int disX=(int)event.getRawX()-startX;//計算偏移的X座標 int disY=(int)event.getRawY()-startY;//計算偏移的Y座標; int left=startLeft+disX; int top=startTop+disY; //更新控制元件位置 layout(left,top,left+getWidth(),top+getHeight()); } break; case MotionEvent.ACTION_UP: break; } //返回true表示不消耗此事件,事件繼續傳遞,返回flase表示事件消耗 return true; } public void setmEventHandle(EventHandle mEventHandle) { this.mEventHandle = mEventHandle; } interface EventHandle{ public void onTouchEvent(MotionEvent event); } }
佈局檔案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="com.example.myapplication.MainActivity">
<com.example.myapplication.SimpleView
android:layout_width="150dp"
android:layout_height="150dp"
/>
</RelativeLayout>
MainActivity,要注意的是,需要在這裡將標題欄去掉,否則拖動的時候會出現偏差
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Window;
public class MainActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);//去掉標題欄
/*
requestWindowFeature(Window.FEATURE_NO_TITLE)無效解決方法:
正常情況下requestWindowFeature(Window.FEATURE_NO_TITLE)是可以生效的,
但是當Activity繼承子AppCompatActivity的時候,這個就失效了
解決辦法:
1、手動在oncreate裡呼叫hide()
if (getSupportActionBar() != null){
getSupportActionBar().hide();
}
*/
if (getSupportActionBar() != null){
getSupportActionBar().hide();
}
setContentView(R.layout.activity_main);
}
}
效果如下圖: