欧美三级国产三级日韩三级_亚洲熟妇丰满大屁股熟妇_欧美亚洲成人一区二区三区_国产精品久久久久久模特

Android開發(fā)之自定義View專題(四):自定義ViewGroup - 新聞資訊 - 云南小程序開發(fā)|云南軟件開發(fā)|云南網站建設-昆明葵宇信息科技有限公司

159-8711-8523

云南網建設/小程序開發(fā)/軟件開發(fā)

知識

不管是網站,軟件還是小程序,都要直接或間接能為您產生價值,我們在追求其視覺表現(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具體代碼:

相關案例查看更多