接下來我詳細(xì)的解說一下我開發(fā)時(shí)寫的這個(gè)實(shí)驗(yàn)demo,軟件中用的滑屏就是由這樣的代碼實(shí)現(xiàn)的。
首先新建一個(gè)控件類TouchPageView并且繼承自ViewGroup,左右滑動(dòng)換屏我的實(shí)現(xiàn)是在TouchPageView添加3個(gè)子view 分別代表看不到的左邊屏幕、可以看到的中間屏幕、看不到的右邊屏幕,這樣在滑屏?xí)r候就可以通過不斷調(diào)整這3個(gè)view的位置實(shí)現(xiàn)連續(xù)不間斷滑屏換屏,下面 的實(shí)驗(yàn)中我分別把3個(gè)view設(shè)置成紅色、綠色、黃色這樣切換的時(shí)候可以看到明顯效果,這3個(gè)view在TouchPageView的構(gòu)造方法中調(diào)用 init方法進(jìn)行初始化:
private void init()
{
views= new ArrayList<LinearLayout>();
view1=new LinearLayout(context);
view1.setBackgroundColor(Color.YELLOW);
this.addView(view1);
TextView tv=new TextView(context);
tv.setText("測(cè)試");
view1.addView(tv);
views.add(view1);
view2=new LinearLayout(context);
view2.setBackgroundColor(Color.RED);
this.addView(view2);
views.add(view2);
view3=new LinearLayout(context);
view3.setBackgroundColor(Color.GREEN);
this.addView(view3);
views.add(view3);
final ViewConfiguration configuration = ViewConfiguration.get(getContext());
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
}
接下來的實(shí)現(xiàn)是關(guān)鍵,重寫onLayout方法對(duì)3個(gè)view的顯示位置布局進(jìn)行控制,通過下面的這個(gè)方法,把3個(gè)view進(jìn)行水平一個(gè)跟著一個(gè)進(jìn)行布局顯示。
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childLeft = -1;
final int count = views.size();
//水平從左到右放置
for (int i = 0; i < count; i++) {
final View child =views.get(i);
if (child.getVisibility() != View.GONE) {
final int childWidth = child.getMeasuredWidth();
if(childLeft==-1)
{
childLeft=-childWidth;
}
child.layout(childLeft, 0, childLeft + childWidth, child.getMeasuredHeight());
childLeft += childWidth;
}
}
}
3個(gè)view位置放置好之后,接下來的實(shí)現(xiàn)實(shí)現(xiàn)手指在屏幕拖拽滑動(dòng)時(shí)讓3個(gè)view跟著手指的位置進(jìn)行變化顯示,這個(gè)肯定是在onTouchEvent 方法中實(shí)現(xiàn)了,分別在MotionEvent.ACTION_DOWN、MotionEvent.ACTION_MOVE、 MotionEvent.ACTION_UP三個(gè)手指狀態(tài)中進(jìn)行控制,在下面的實(shí)現(xiàn)中還采用了VelocityTracker的方法對(duì)手指的滑動(dòng)速度進(jìn)行 跟蹤,這樣根據(jù)滑動(dòng)速度決定屏幕往哪個(gè)方向換屏,關(guān)鍵的代碼如下:
@Override
public boolean onTouchEvent(MotionEvent ev){
if(!lock)
{
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev);
final int action = ev.getAction();
final float x = ev.getX();
final float y = ev.getY();
switch (action) {
case MotionEvent.ACTION_DOWN://按下去
if(touchState==TOUCH_STATE_REST)
{
//記錄按下去的的x坐標(biāo)
lastMotionX = x;
touchState=TOUCH_STATE_MOVING;
isMoved=false;
}
break;
case MotionEvent.ACTION_MOVE://拖動(dòng)時(shí)
if(touchState==TOUCH_STATE_MOVING)
{
float offsetX=x-lastMotionX;
float offsetY=y-lastMotionY;
if(isMoved)
{
lastMotionX=x;
lastMotionY=y;
final int count = views.size();
//水平從左到右放置
for (int i = 0; i < count; i++) {
final View child =views.get(i);
if (child.getVisibility() != View.GONE) {
final int childWidth = child.getMeasuredWidth();
int childLeft = child.getLeft()+(int)offsetX;
child.layout(childLeft, 0, childLeft + childWidth, child.getMeasuredHeight());
childLeft += childWidth;
}
}
}
else if(Math.abs(offsetX)>TOUCH_SLOP||Math.abs(offsetY)>TOUCH_SLOP)
{
//移動(dòng)超過閾值,則表示移動(dòng)了
isMoved=true;
removeCallbacks(mLongPressRunnable);
}
}
break;
case MotionEvent.ACTION_UP://放開時(shí)
//釋放了
removeCallbacks(mLongPressRunnable);
if(isMoved)
{
if(touchState==TOUCH_STATE_MOVING)
{
touchState=TOUCH_STATE_SLOWING;
int sign=0;
final VelocityTracker velocityTracker = mVelocityTracker;
//計(jì)算當(dāng)前速度
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
//x方向的速度
int velocityX = (int) velocityTracker.getXVelocity();
if(velocityX > SNAP_VELOCITY)//足夠的能力向左
{
sign=1;
Log.e("enough to move left", "true");
}
else if (velocityX < -SNAP_VELOCITY)//足夠的能力向右
{
sign=-1;
Log.e("enough to move right", "right");
}
else
{
sign=0;
}
moveToFitView(sign);
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
}
break;
}
}
return true;
}
完成手指滑的功能后,最后在手指離開屏幕的時(shí)候,讓3個(gè)view滑動(dòng)到合適的位置,保證當(dāng)前屏幕只能看到一個(gè)完整的view另外2個(gè)view不可見,并 且在滑動(dòng)的過程中為了達(dá)到比較自然的效果,采用減速滑動(dòng)的實(shí)現(xiàn),這里是用了Handler進(jìn)行間隔的減速移動(dòng)效果,這樣滑動(dòng)起來比較舒服,其實(shí)最好的效果 應(yīng)該加入阻尼效果,就是讓view一定程度的沖過屏幕邊界然后在回彈,經(jīng)過幾次這樣的緩減至速度為零然后最終停止,這個(gè)可以由各位自己去實(shí)現(xiàn),并不難寫。
int offset=0;
private void moveToFitView(int sign)
{
boolean b=swapView(sign);
if(true)
{
View view1=views.get(1);
int left=view1.getLeft();
//int offset=0;
if(left!=0)
{
offset=-1*left;
}
moveView();
}
}
FlipAnimationHandler mAnimationHandler;
int ovv=40;
private void moveView()
{
final int count = views.size();
if(offset!=0)
{
int ov=0;
if(offset>0)
{
ov=ovv;
}
else
{
ov=-1*ovv;
}
ovv=ovv-3;
if(ovv<1)
{
ovv=3;
}
if(Math.abs(offset)<Math.abs(ov))
{
ov=offset;
offset=0;
}
else
{
offset=offset-ov;
}
//水平從左到右放置
for (int i = 0; i < count; i++) {
final View child =views.get(i);
final int childWidth = child.getMeasuredWidth();
int childLeft = child.getLeft()+ov;
child.layout(childLeft, 0, childLeft + childWidth, child.getMeasuredHeight());
childLeft += childWidth;
}
if(mAnimationHandler==null)
{
mAnimationHandler = new FlipAnimationHandler();
}
mAnimationHandler.sleep(1);
}
else
{
ovv=40;
touchState=TOUCH_STATE_REST;
}
}
class FlipAnimationHandler extends Handler {
@Override
public void handleMessage(Message msg) {
TouchPageView.this.moveView();
}
public void sleep(long millis) {
this.removeMessages(0);
sendMessageDelayed(obtainMessage(0), millis);
}
}
整個(gè)自定義控件核心的思路和代碼就上面這些了,實(shí)現(xiàn)效果請(qǐng)參看我的微讀效果。
完整的代碼:
package xx.weidu;import java.util.ArrayList;import java.util.List;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.os.Handler;import android.os.Message;import android.util.Log;import android.view.MotionEvent;import android.view.VelocityTracker;import android.view.View;import android.view.ViewConfiguration;import android.view.ViewGroup;import android.widget.LinearLayout;import android.widget.TextView;public class TouchPageView extends ViewGroup{ private LinearLayout view1; private LinearLayout view2; private LinearLayout view3; //速度跟蹤 private VelocityTracker mVelocityTracker; private int mMaximumVelocity; //手勢(shì)臨界速度,當(dāng)速度超過這個(gè)時(shí)切換到下一屏 private static final int SNAP_VELOCITY = 100; //停止?fàn)顟B(tài) private final static int TOUCH_STATE_REST = 0; //滾動(dòng)狀態(tài) private final static int TOUCH_STATE_MOVING = 1; //減速停止?fàn)顟B(tài) private final static int TOUCH_STATE_SLOWING = 2; //當(dāng)前觸摸狀態(tài) private int touchState = TOUCH_STATE_REST; private boolean lock=false; private float lastMotionX; private float lastMotionY; private Context context; private List<LinearLayout> views; //是否移動(dòng)了 private boolean isMoved; //長(zhǎng)按的runnable private Runnable mLongPressRunnable; //移動(dòng)的閾值 private static final int TOUCH_SLOP=10; public int width; public int height; public TouchPageView(Context context) { super(context); this.context=context; init(); } private void init() { views= new ArrayList<LinearLayout>(); view1=new LinearLayout(context); view1.setBackgroundColor(Color.YELLOW); this.addView(view1); TextView tv=new TextView(context); tv.setText("測(cè)試"); view1.addView(tv); views.add(view1); view2=new LinearLayout(context); view2.setBackgroundColor(Color.RED); this.addView(view2); views.add(view2); view3=new LinearLayout(context); view3.setBackgroundColor(Color.GREEN); this.addView(view3); views.add(view3); final ViewConfiguration configuration = ViewConfiguration.get(getContext()); mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){ super.onMeasure(widthMeasureSpec, heightMeasureSpec); final int count = views.size(); for (int i = 0; i < count; i++) { final View child =views.get(i); child.measure(widthMeasureSpec,heightMeasureSpec); } int finalWidth, finalHeight; finalWidth = measureWidth(widthMeasureSpec); finalHeight = measureHeight(heightMeasureSpec); this.width=finalWidth; this.height=finalHeight; } private int measureWidth(int measureSpec) { int result = 0; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) { result = specSize; } else { result = specSize; } return result; } private int measureHeight(int measureSpec) { int result = 0; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) { result = specSize; } else { result = specSize; } return result; } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int childLeft = -1; final int count = views.size(); //水平從左到右放置 for (int i = 0; i < count; i++) { final View child =views.get(i); if (child.getVisibility() != View.GONE) { final int childWidth = child.getMeasuredWidth(); if(childLeft==-1) { childLeft=-childWidth; } child.layout(childLeft, 0, childLeft + childWidth, child.getMeasuredHeight()); childLeft += childWidth; } } } //繪制子元素 @Override protected void onDraw(Canvas canvas) { //水平從左到右放置 int count = views.size(); for (int i = 0; i < count; i++) { View child =views.get(i); drawChild(canvas, child, getDrawingTime()); } } @Override public boolean onTouchEvent(MotionEvent ev){ if(!lock) { if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(ev); final int action = ev.getAction(); final float x = ev.getX(); final float y = ev.getY(); switch (action) { case MotionEvent.ACTION_DOWN://按下去 if(touchState==TOUCH_STATE_REST) { //記錄按下去的的x坐標(biāo) lastMotionX = x; touchState=TOUCH_STATE_MOVING; isMoved=false; } break; case MotionEvent.ACTION_MOVE://拖動(dòng)時(shí) if(touchState==TOUCH_STATE_MOVING) { float offsetX=x-lastMotionX; float offsetY=y-lastMotionY; if(isMoved) { lastMotionX=x; lastMotionY=y; final int count = views.size(); //水平從左到右放置 for (int i = 0; i < count; i++) { final View child =views.get(i); if (child.getVisibility() != View.GONE) { final int childWidth = child.getMeasuredWidth(); int childLeft = child.getLeft()+(int)offsetX; child.layout(childLeft, 0, childLeft + childWidth, child.getMeasuredHeight()); childLeft += childWidth; } } } else if(Math.abs(offsetX)>TOUCH_SLOP||Math.abs(offsetY)>TOUCH_SLOP) { //移動(dòng)超過閾值,則表示移動(dòng)了 isMoved=true; removeCallbacks(mLongPressRunnable); } } break; case MotionEvent.ACTION_UP://放開時(shí) //釋放了 removeCallbacks(mLongPressRunnable); if(isMoved) { if(touchState==TOUCH_STATE_MOVING) { touchState=TOUCH_STATE_SLOWING; int sign=0; final VelocityTracker velocityTracker = mVelocityTracker; //計(jì)算當(dāng)前速度 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); //x方向的速度 int velocityX = (int) velocityTracker.getXVelocity(); if(velocityX > SNAP_VELOCITY)//足夠的能力向左 { sign=1; Log.e("enough to move left", "true"); } else if (velocityX < -SNAP_VELOCITY)//足夠的能力向右 { sign=-1; Log.e("enough to move right", "right"); } else { sign=0; } moveToFitView(sign); if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } } } break; } } return true; } int offset=0; private void moveToFitView(int sign) { boolean b=swapView(sign); if(true) { View view1=views.get(1); int left=view1.getLeft(); //int offset=0; if(left!=0) { offset=-1*left; } moveView(); } } FlipAnimationHandler mAnimationHandler; int ovv=40; private void moveView() { final int count = views.size(); if(offset!=0) { int ov=0; if(offset>0) { ov=ovv; } else { ov=-1*ovv; } ovv=ovv-3; if(ovv<1) { ovv=3; } if(Math.abs(offset)<Math.abs(ov)) { ov=offset; offset=0; } else { offset=offset-ov; } //水平從左到右放置 for (int i = 0; i < count; i++) { final View child =views.get(i); final int childWidth = child.getMeasuredWidth(); int childLeft = child.getLeft()+ov; child.layout(childLeft, 0, childLeft + childWidth, child.getMeasuredHeight()); childLeft += childWidth; } if(mAnimationHandler==null) { mAnimationHandler = new FlipAnimationHandler(); } mAnimationHandler.sleep(1); } else { ovv=40; touchState=TOUCH_STATE_REST; } } class FlipAnimationHandler extends Handler { @Override public void handleMessage(Message msg) { TouchPageView.this.moveView(); } public void sleep(long millis) { this.removeMessages(0); sendMessageDelayed(obtainMessage(0), millis); } } private boolean swapView(int sign) { boolean b=false; if(sign==-1)//向左 { View view0=views.get(0); if(view0.getLeft()<=-1*view0.getMeasuredWidth()) { swapViewIndex(sign); View view2=views.get(1); View view3=views.get(2); int childWidth=view2.getMeasuredWidth(); int childLeft=view2.getLeft()+childWidth; view3.layout(childLeft, 0, childLeft + view3.getMeasuredWidth(), view3.getMeasuredHeight()); b=true; } } else if(sign==1)//向右 { View view3=views.get(2); if(view3.getLeft()>view3.getMeasuredWidth()) { swapViewIndex(sign); View view1=views.get(0); View view2=views.get(1); int childRight=view2.getLeft(); int childLeft=childRight-view1.getMeasuredWidth(); view1.layout(childLeft, 0, childRight, view1.getMeasuredHeight()); b=true; } } return b; } private void swapViewIndex(int sign) { if(sign==-1)//向左 { LinearLayout v=views.remove(0); views.add(v); } else if(sign==1)//向右 { LinearLayout v=views.remove(views.size()-1); views.add(0, v); } }} |

