知識
不管是網站,軟件還是小程序,都要直接或間接能為您產生價值,我們在追求其視覺表現(xiàn)的同時,更側重于功能的便捷,營銷的便利,運營的高效,讓網站成為營銷工具,讓軟件能切實提升企業(yè)內部管理水平和效率。優(yōu)秀的程序為后期升級提供便捷的支持!
Android開發(fā)之自定義View專題(四):自定義ViewGroup
發(fā)表時間:2020-10-19
發(fā)布人:葵宇科技
瀏覽次數(shù):47
有時刻,我們會有如許的需求,一個activity瑯綾擎須要有兩個或者多個界面切換,就像Viewpager那樣。然則在這些界面瑯綾擎又須要可以或許有l(wèi)istView,gridview等組件。如不蚜鱭向的,似乎還好,沒什么竽暌拱響,那么如不雅是橫向的,那么就會出工作。因為Viewpager會攔截觸摸事宜。而如不雅將Viewpager的觸摸事沂攀攔截掉落給瑯綾擎的子控件,那么Viewpager又不克不及響應滑動事宜了。那么若何又能讓界面之間可以或許往返切換,又能讓瑯綾擎的子控件的觸摸事宜也能毫無影響的響應呢,這個時刻,我們須要自定義一個Viewgroup,重寫瑯綾擎的觸摸攔截辦法即可。
博主自定義的ViewGroup類似于SlideMenu,包含兩個界面的往返切換,博主特意放了一個可以橫向滑動item的listView組件在的個界面實驗,這個listView控件也是博主之前大年夜網上找出來竽暌姑的,還不錯的一個控件。具體看效不雅圖:
[img]http://img.blog.csdn.net/20150105195005125?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdmljdG9yZnJlZWRvbQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center[img]http://img.blog.csdn.net/20150105195027482?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdmljdG9yZnJlZWRvbQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
好了,慣例子,完全項面前目今載地址:
http://download.csdn.net/detail/victorfreedom/8329667
有興趣的同窗可以下載下來研究進修。
自定義ViewGroup具體代碼:
package com.freedom.slideviewgroup.ui; import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.animation.AccelerateInterpolator; import android.widget.Scroller; import com.freedom.slideviewgroup.FreedomApplication; import com.freedom.slideviewgroup.utils.DptoPxUtil; /** * @ClassName: SlideMenu * @author victor_freedom ([email protected]) * @createddate 2015-1-5 下晝8:00:36 * @Description: TODO */ public class SlideMenu extends ViewGroup { private Context mContext; // 默認第一個 private int currentScreen = 0; // 當前屏 // 移動控制者 private Scroller mScroller = null; // 斷定是否可以移動 private boolean canScroll = false; // 是否攔截點擊事宜,false表示攔截,true表示將點擊事宜傳遞給子控件 private boolean toChild = false; // 處理觸摸事宜的移動速度標準 public static int SNAP_VELOCITY = 600; // 觸發(fā)move的最小滑動距離 private int mTouchSlop = 0; // 最后一點的X坐標 private float mLastionMotionX = 0; // 處理觸摸的速度 private VelocityTracker mVelocityTracker = null; // 閣下子控件的監(jiān)聽器 private LeftListener leftListener; private RightListener rightListener; // 觸摸狀況 private static final int TOUCH_STATE_REST = 0; private static final int TOUCH_STATE_SCROLLING = 1; private int mTouchState = TOUCH_STATE_REST; // 響應觸摸事宜的邊距剖斷距離(這個根據自定義響應) public static int TOHCH_LEFT = 140; public static int TOHCH_RIGHT = FreedomApplication.mScreenWidth; public static final String TAG = "SlideMenu"; public SlideMenu(Context context) { super(context); mContext = context; init(); } public SlideMenu(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; init(); } /** * @Title: init * @Description: 初始化滑動相干的器械 * @throws */ private void init() { mScroller = new Scroller(mContext, new AccelerateInterpolator()); mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); } /** * @Title: onMeasure * @Description: 設定viewGroup大年夜小 * @param widthMeasureSpec * @param heightMeasureSpec * @throws */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); setMeasuredDimension(width, height); for (int i = 0; i < getChildCount(); i++) { getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec); } } /** * @Title: onLayout * @Description: 設置子控件的分布地位 * @param changed * @param l * left * @param t * top * @param r * right * @param b * bottom * @throws */ @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int startLeft = 0; // 每個子視圖的肇端構造坐標 int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View child = getChildAt(i); child.layout(startLeft, 0, startLeft + getWidth(), getHeight()); startLeft = startLeft + getWidth(); // 校準每個子View的肇端構造地位 } } /** * @Title: onInterceptTouchEvent * @Description:觸摸事沂攀攔截剖斷 * @param ev * @return * @throws */ @Override public boolean onInterceptTouchEvent(MotionEvent ev) { final int action = ev.getAction(); // 如不雅當前正在滑動狀況,則攔截事宜 if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) { return true; } final float x = ev.getX(); switch (action) { case MotionEvent.ACTION_DOWN: mLastionMotionX = x; // 斷定當緇ご態(tài) mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING; // 斷定是否可以或許響應滑動事宜 if ((ev.getX() < DptoPxUtil.dip2px(mContext, TOHCH_LEFT) && currentScreen == 1) || (ev.getX() > TOHCH_RIGHT - DptoPxUtil.dip2px(mContext, 200) && currentScreen == 0)) { canScroll = true; toChild = false; return super.onInterceptTouchEvent(ev); } else { // 如不雅不克不及則不攔截事宜 canScroll = false; toChild = true; return false; } case MotionEvent.ACTION_MOVE: if (toChild) { return false; } final int differentX = (int) Math.abs(mLastionMotionX - x); // 跨越了最小滑動距離,并且沒有傳遞事宜給子控件,則更改狀況 if (differentX > mTouchSlop) { mTouchState = TOUCH_STATE_SCROLLING; } break; case MotionEvent.ACTION_UP: if (toChild) { return false; } mTouchState = TOUCH_STATE_REST; break; } return super.onInterceptTouchEvent(ev); } /** * @Title: onTouchEvent * @Description: 觸摸事宜響應 * @param event * @return * @throws */ public boolean onTouchEvent(MotionEvent event) { if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } // 獲取移動的速度 mVelocityTracker.addMovement(event); super.onTouchEvent(event); // 手指地位地點 float x = event.getX(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // 如不雅屏幕的動畫還沒停止,你就按下了,我們就停止該動畫 if (mScroller != null) { if (!mScroller.isFinished()) { mScroller.abortAnimation(); } } mLastionMotionX = x; break; case MotionEvent.ACTION_MOVE: if (canScroll) { int detaX = (int) (mLastionMotionX - x); mLastionMotionX = x; // 移動距離 scrollBy(detaX, 0); } break; case MotionEvent.ACTION_UP: final VelocityTracker velocityTracker = mVelocityTracker; velocityTracker.computeCurrentVelocity(1000); int velocityX = (int) velocityTracker.getXVelocity(); // 滑動速度達到了一個標準(快速向右滑屏,返回上一個屏幕) 立時進行切屏處理 if (velocityX > SNAP_VELOCITY && currentScreen > 0 && canScroll) { changedScreen(currentScreen - 1); } // 快速向左滑屏,返回下一屏幕) else if (velocityX < -SNAP_VELOCITY && currentScreen < (getChildCount() - 1) && canScroll) { changedScreen(currentScreen + 1); } // 以上為快速移動的 ,強迫切換屏幕 else { // 如不雅移動遲緩,那么先斷定是保存在本屏幕照樣到下一屏幕 snapToDestination(); } if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } mTouchState = TOUCH_STATE_REST; break; case MotionEvent.ACTION_CANCEL: mTouchState = TOUCH_STATE_REST; break; } return super.onInterceptTouchEvent(event); } @Override public void computeScroll() { if (mScroller.computeScrollOffset()) {// 如不雅返回true,則代表正在模仿數(shù)據,false表示已經停止模仿數(shù)據 scrollTo(mScroller.getCurrX(), mScroller.getCurrY());// 更新偏移量 postInvalidate(); } } /** * @Title: startMove * @Description: 這是大年夜第一個屏幕跳轉到第二個屏幕的快捷辦法 * @throws */ public void startMove() { if (currentScreen == 1) { return; } if (currentScreen == 0 && rightListener != null) { new Thread(new Runnable() { @Override public void run() { rightListener.postNotifyDataChange(); } }).start(); } currentScreen++; mScroller.startScroll((currentScreen - 1) * getWidth(), 0, getWidth(), 0, 600); // 刷新界面 invalidate();// invalidate -> drawChild -> child.draw -> computeScroll } /** * @Title: startMoves * @Description: 跳轉到第一個屏幕 * @throws */ public void startMoves() { changedScreen(0); } /** * @Title: snapToDestination * @Description: 當遲緩移動的時刻,斷定跳轉屏幕 * @throws */ private void snapToDestination() { int destScreen = (getScrollX() + getWidth() / 3) / getWidth(); changedScreen(destScreen); } /** * @Title: changedScreen * @Description: 跳轉屏幕 * @param whichScreen * @throws */ private void changedScreen(int whichScreen) { currentScreen = whichScreen; if (currentScreen > getChildCount() - 1) { currentScreen = getChildCount() - 1; } if (currentScreen == 0 && leftListener != null) { leftListener.notifyDataChange(); } if (currentScreen == 1 && rightListener != null) { rightListener.notifyDataChange(); } // getScrollX獲得的是當前視圖相對于父控件的偏移量。初始值是0, int dx = currentScreen * getWidth() - getScrollX(); // dx為正值甌,屏幕向右滑動,dx為負值甌,屏幕向左滑動 mScroller.startScroll(getScrollX(), 0, dx, 0, 600); postInvalidate(); } public interface LeftListener { public void notifyDataChange(); } public interface RightListener { public void notifyDataChange(); public void postNotifyDataChange(); } public void setLeftListener(LeftListener leftListener) { this.leftListener = leftListener; } public void setRightListener(RightListener rightListener) { this.rightListener = rightListener; } }
自此自定義View專題已經講解完畢,信賴所有人都對自定義View有了一個初步的熟悉,根本上就是那么幾個步調。而自定義ViewGroup相對于自定義View多了一個步調在于要重寫onLayout辦法來擺放包含在瑯綾擎的子控件,其余的,都差不多。
好了,慣例子,完全項面前目今載地址:
http://download.csdn.net/detail/victorfreedom/8329667
有興趣的同窗可以下載下來研究進修。
自定義ViewGroup具體代碼: