這個例子中涉及到了以下幾個知識點:
1) attrs.xml檔案的使用
2) GestureDetector.OnGestureListener監聽手勢
3) onLayout()、onMeasure()、onTouchEvent()的使用
接下來說一下我實現的思路:
1) 每個案頭就是一個大組件,水平的排列線上性布局檔案中,每個案頭適合螢幕一樣大小,所以要拓展LinearLayout,重寫其中的onMeasure()、onLayout()方法
2) 由於要實現隨手勢滑動,所以只要實現GestureDetector.OnGestureListener介面中的onDown()、onScroll()方法就可以
3) 由於要接收觸屏事件,所以要實現onTouchEvent()
接下來我們來看一下代碼吧:
- public class ScrollLayout extends LinearLayout implements GestureDetector.OnGestureListener{
- private int offset; //相對距離
- private GestureDetector gestureDetector; //手勢事件
- private int childWidth; //子View的寬度
- private int childCount; //子視圖的數量
- private int defaultWindow; //預設視窗
- private boolean setShareWindowFlag=false; // 保證預設視窗的設定只執行一次
- public ScrollLayout(Context context) {
- super(context);
- init();
- }
- public ScrollLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- init();
- //擷取定義的defaultWindow的值
- TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyScrollWindow);
- defaultWindow = typedArray.getInteger(R.styleable.MyScrollWindow_defaultWindow,0);
- }
- private void init(){
- gestureDetector = new GestureDetector(this.getContext(),this);
- }
- //傳回值為true 才能觸發 手勢事件
- public boolean onDown(MotionEvent e) {
- return true;
- }
- public void onShowPress(MotionEvent e) { }
- public boolean onSingleTapUp(MotionEvent e) {
- return false;
- }
- //順手勢滑動
- public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
- //擷取滑動的距離
- offset = (int) (offset - distanceX);
- //防止滑出邊界
- if(offset>0){
- offset=0;
- }else if(offset < -1*childWidth*(childCount-1)){
- offset= -1*childWidth*(childCount-1);
- }
- //重繪布局
- requestLayout();
- return true;
- }
- public void onLongPress(MotionEvent e) {
-
- }
- public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
- return false;
- }
- //設定布局檔案的寬高和每個案頭的寬高
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec,heightMeasureSpec);
- //給每個案頭設定和螢幕相同的寬度和高度
- int childCount = getChildCount();
- for(int i=0;i<childCount;i++){
- getChildAt(i).measure(widthMeasureSpec,heightMeasureSpec);
- }
- }
- //設定布局
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- childCount = getChildCount(); //擷取子視圖數
- childWidth = childCount > 0? getChildAt(0).getMeasuredWidth():0; //擷取位元組點的寬度
- if(!setShareWindowFlag&&defaultWindow>=0&&defaultWindow<=childCount-1){
- //設定預設視窗的左端距離
- offset = -1*defaultWindow*childWidth;
- setShareWindowFlag=true;
- }
- //設定距離(0,0)點X軸方向的初始距離
- int left = 0+offset;
- for (int i = 0; i < childCount ; i ++){
- //設定每個子視圖在布局中的位置
- View child = getChildAt(i);
- if(child.getVisibility()!=View.GONE){
- child.layout(left,0,childWidth+left,child.getMeasuredHeight());
- left = left + childWidth;
- }
- }
- }
- //觸屏事件
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- boolean result = gestureDetector.onTouchEvent(event);
- if(event.getAction()==MotionEvent.ACTION_UP){
- //當手指抬起來時 判斷滑動距離顯示整個子視圖
- showOneDesktop();
- }
- return result;
- }
- //判斷當手指抬起時顯示那個案頭
- private void showOneDesktop(){
- int index = Math.abs(offset)/childWidth;
- if(Math.abs(offset)-index*childWidth>childWidth/2){
- index++;
- }
- offset = offset > 0 ? index*childWidth : -1*index*childWidth;
- requestLayout();
- }
- }
複製代碼
在這段代碼中使用到的attrs.xml檔案:
- <resources>
- <declare-styleable name="MyScrollWindow">
- <attr name="defaultWindow" format="integer"/>
- </declare-styleable>
- </resources>
複製代碼
可以和系統內建的布局檔案一樣使用
- <?xml version="1.0" encoding="utf-8"?>
- <com.wxg.scroll_window.view.ScrollLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:demo="http://schemas.android.com/apk/res/com.wxg.scroll_window"
- android:id="@+id/testLayout"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- demo:defaultWindow="1"
- >
- <FrameLayout
- android:layout_height="fill_parent"
- android:layout_width="fill_parent"
- android:background="#cccccc">
- <Button
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:text="1"/>
- </FrameLayout>
- <FrameLayout
- android:layout_height="fill_parent"
- android:layout_width="fill_parent"
- android:background="#ffffff">
- <Button
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:text="2"/>
- </FrameLayout>
- <FrameLayout
- android:layout_height="fill_parent"
- android:layout_width="fill_parent"
- android:background="#bcbcbc">
- <Button
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:text="3"/>
- </FrameLayout>
- </com.wxg.scroll_window.view.ScrollLayout>