先寫出自定義View界面
代碼如下:先創(chuàng)建一個(gè)View繼承View,然后重寫測量方法去測量出要顯示的真是高度,再重寫繪制方法,去把這個(gè)按鈕繪制在什么位置。然后就是為我們后續(xù)的觸摸事件做準(zhǔn)備,重寫OnTouchEvent()
package com.example.z.selfscrollunlock;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
* Created by z on 2017/11/25.
*/
public class SelfUnLockView extends View {
private Bitmap mBitmap;
private int mHeight;
private int mWidth;
private Paint mPaint;
private int mWidth1;
public SelfUnLockView(Context context) {
this(context, null);
}
public SelfUnLockView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.switch_button);
mHeight = mBitmap.getHeight();
mWidth = mBitmap.getWidth();
mPaint = new Paint();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
mWidth1 = MeasureSpec.makeMeasureSpec(widthMeasureSpec, MeasureSpec.EXACTLY);
setMeasuredDimension(mWidth1, mHeight);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(mBitmap, 0, 0, mPaint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
float rawX = event.getRawX();
//當(dāng)我手指觸摸到滑塊時(shí)候,滑塊過來要移動這個(gè)點(diǎn)減去滑塊一般的長度 raw
float moveX = rawX - mWidth / 2;
scrollTo((int) -moveX,0);
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
break;
}
return super.onTouchEvent(event);
}
}
在自定義View 的構(gòu)造方法中,我們?nèi)コ跏蓟粋€(gè)畫筆,初始化背景的 長 寬。在onTouch()中,處理觸摸事件,在手指按下的時(shí)候,讓滑塊中間位置移動到觸摸點(diǎn):注意,這個(gè)ScrollTo方法,往右邊移動是減去一個(gè)數(shù),可以想成,水調(diào)用這個(gè)Scroll方法,是this ,當(dāng)前自定義View。你簡單記憶成這個(gè)移動是移動他的爹,滑塊向右移動,你就當(dāng)成是滑塊不動,他爹應(yīng)該是往那邊移動才是我們想要的結(jié)果,他爹是往左邊移動才行。我們的左邊是右邊正,往左了肯定是負(fù)。效果如下:
但是很多問題,比如越界問題,回彈等,我們一步一步處理
處理點(diǎn)擊滑塊外部不響應(yīng)
把這個(gè)滑動事件判斷一下執(zhí)行:
if (rawX > 0 && rawX < mWidth) {
scrollTo((int) -moveX, 0);
}
處理左邊越界問題
更改一個(gè)地方:
if (rawX > mWidth/2 && rawX < mWidth) {
處理滑塊滑動
在ontouch事件的move 動作中處理:并處理滑動越界問題
case MotionEvent.ACTION_MOVE:
//處理越界問題
float rawXMove = event.getRawX();//
if(rawXMove >mWidth/2 && rawXMove <=getMeasuredWidth()-mWidth/2){
float dexX = rawXMove - mWidth/2; //變化量
scrollTo((int) ( - dexX), 0);
}
break;
注意上面的滑動越界問題,我是通過手指的位置判斷的,當(dāng)然你可以通過一個(gè)增加量來判斷是否滿足條件,如果滿足不滿足條件直接把偏移量置零或者最大 。注意上面的判斷有個(gè)細(xì)節(jié),我使用的是getMeasuredWidth( ) 來處理,并不是用的mWidth1,如果你是用后者,你會發(fā)現(xiàn)這個(gè)值非常大。會越界。這里的getMeasuredWidth()拿的值就是控件背景寬度。如果弄不明白就直接使用增加量判斷
你的滑塊是否能滑動呢?
如果你的滑塊能點(diǎn)擊,不能滑動,注意看你的自定義控件是否消費(fèi)事件,如果沒有消費(fèi)事件,就是掉super方法,肯定不會處理,所以你要返回值改為true.消費(fèi)時(shí)間才行,因?yàn)榛瑒邮录忘c(diǎn)擊事件不同的!
滑塊的回原點(diǎn)
case MotionEvent.ACTION_UP:
float endRawX = event.getRawX();
if(endRawX <getWidth() - mWidth/2 ){
scrollTo(0,0);
}else {
Toast.makeText(getContext(),"解鎖成功",Toast.LENGTH_LONG).show();
}
解鎖不成功,緩慢回彈
1.創(chuàng)建Scroller對象
2.調(diào)用startScroll 方法去滾動 ,配置滾動信息
3.出發(fā)重新繪制調(diào)用 invalidate();
4.計(jì)算滾動的偏移量。重寫computerScroll( ) ,根據(jù)當(dāng)前已過時(shí)間計(jì)算偏移量 返回的是boolean值,如果沒結(jié)束,就是滾動還沒有結(jié)束, scroller.scrollTo(X,getcriiX(),0)然后出發(fā)繪制,完整代碼如下:
package com.example.z.selfscrollunlock;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Scroller;
import android.widget.Toast;
/**
* Created by z on 2017/11/25.
*/
public class SelfUnLockView extends View {
private Bitmap mBitmap;
private int mHeight;
private int mWidth;
private Paint mPaint;
private int mWidth1;
private float mRawX;
private Scroller mScroller;
private ScrollListener mListener;
public SelfUnLockView(Context context) {
this(context, null);
}
public SelfUnLockView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.switch_button);
mHeight = mBitmap.getHeight();
mWidth = mBitmap.getWidth();
mPaint = new Paint();
mScroller = new Scroller(getContext());
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
mWidth1 = MeasureSpec.makeMeasureSpec(widthMeasureSpec, MeasureSpec.EXACTLY);
setMeasuredDimension(mWidth1, mHeight);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(mBitmap, 0, 0, mPaint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mRawX = event.getRawX();
//當(dāng)我手指觸摸到滑塊時(shí)候,滑塊過來要移動這個(gè)點(diǎn)減去滑塊一般的長度 raw
float moveX = mRawX - mWidth / 2;
if (mRawX > mWidth / 2 && mRawX < mWidth) {
scrollTo((int) -moveX, 0);
}
break;
case MotionEvent.ACTION_MOVE:
//處理越界問題
float rawXMove = event.getRawX();//
if(rawXMove >mWidth/2 && rawXMove <=getMeasuredWidth()-mWidth/2){
float dexX = rawXMove - mWidth/2; //變化量
scrollTo((int) ( - dexX), 0);
}
break;
case MotionEvent.ACTION_UP:
float endRawX = event.getRawX();
if(endRawX <getWidth() - mWidth/2 ){
/* mScroller.startScroll(getScrollX(),0,0-getScrollX(),0,500); //配置滾動信息
// invalidate();
// scrollTo(0,0);*/
int startX = getScrollX();//滾動開始時(shí)滾動偏移量
int startY = 0;
int endX = 0;//滾動最終位置的滾動偏移量
int dx = endX - startX;//滾動偏移量的變化量
int dy =0;
int duration = 1500;//滾動時(shí)長
//step2:配置滾動的成員變量
mScroller.startScroll(startX, startY, dx, dy, duration);
invalidate();//step3:觸發(fā)重新繪制
}else {
Toast.makeText(getContext(),"解鎖成功",Toast.LENGTH_LONG).show();
/** 出發(fā)接口方法*/
if(mListener != null){
mListener.onScrollunLock();
}
}
break;
}
return true;
}
@Override
public void computeScroll() {
if(mScroller.computeScrollOffset()){
scrollTo(mScroller.getCurrX(),0);
invalidate();
}
}
/**
* 回調(diào)函數(shù)
*/
public interface ScrollListener{
void onScrollunLock();
}
/**
* 宮外不調(diào)用的方法
*/
public void onScrollViewMethed(ScrollListener l){
this.mListener = l;
}
}
測滑菜單
需求分析:
1.繼承Viewgroup
2.測量布局孩子
3.處理越界問題
package com.example.z.selfscrollunlock;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
/**
* Created by z on 2017/11/25.
*/
public class SlidingMenu extends ViewGroup {
private float mRawX;
private View mRight;
private View mLeft;
private int mMeasuredWidth;
private int mMeasuredWidth1;
public SlidingMenu(Context context) {
this(context, null);
}
public SlidingMenu(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
measureChildren(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
mLeft = getChildAt(0);
mRight = getChildAt(1);
mMeasuredWidth = mLeft.getMeasuredWidth();
mMeasuredWidth1 = mRight.getMeasuredWidth();
int measuredHeight = mLeft.getMeasuredHeight();
int measuredHeight1 = mRight.getMeasuredHeight();
mLeft.layout(-mMeasuredWidth, 0, 0, measuredHeight);
mRight.layout(0, 0, mMeasuredWidth1, measuredHeight1);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mRawX = event.getRawX();
break;
case MotionEvent.ACTION_MOVE:
float rawX1 = event.getRawX();
float v = -rawX1 + mRawX; //滾動偏移量
float finall = v + getScrollX(); //最終偏移量
if(finall >0){
scrollTo(0,0);
}else {
scrollTo(-mMeasuredWidth,0);
}
return true;
//
// int i = mMeasuredWidth1 - mMeasuredWidth;
// if (v <-i) {
// v = i;
// }
// else if (v < -mMeasuredWidth) {
// v = 0;
// }
//// scrollTo((int) -v, 0);
// scrollBy((int) -v, 0);
// mRawX = rawX1;
}
return true;
}
}