国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android自定義控件案例匯總2(自定義開關、下拉刷新、側滑菜單)

2019-12-12 04:25:47
字體:
來源:轉載
供稿:網友

案例四 自定義開關:

功能介紹:本案例實現的功能是創建一個自定義的開關,可以自行決定開關的背景。當滑動開關時,開關的滑塊可跟隨手指移動。當手指松開后,滑塊根據開關的狀態,滑到最右邊或者滑到最左邊,同時保存開關的狀態,將開關的狀態回調給調用者。當然,上述功能系統給定的switch控件也可以實現。

實現步驟:

        1. 寫一個類繼承view,重寫兩個參數的構造方法。在構造方法中指定工作空間,通過attrs.getAttributeResourceValue方法將java代碼中的屬性值和xml中的屬性值聯系起來。這樣可以在xml文件中指定相關的屬性值。重寫onmeasure和ondraw方法,繪制圖片。這里測量圖片大小直接用setMeasuredDimension方法,獲取圖片本身的大小。
        2. 設置接口回調。對于圖片來說,我們希望能夠在調用者獲取開關的狀態,因此需要設置一個接口回調,用于監控開關的狀態,當開關的狀態發生變化時間調用。接口回調的優勢在于調用者并不知道何時調用,所以在另一個文件中設置一個接口,在該文件觸發事件。由于重寫了接口的方法,因此,執行重寫后的方法。這樣就可以實現數據的回調。自定義控件中接口回調的應用較為廣泛,幾乎所有的控件都需要設置監聽,且寫法較為固定。
        3. 重寫ontouchevent()方法。分析得知,開關由兩部分組成,一部分是底座兒,一部分是劃片而。當手指滑動時,劃片兒應該跟隨手指移動。當劃片的左邊小于底座左邊坐標時,讓劃片左邊的坐標和底座對齊,當劃片的右邊大于底座右邊坐標時,讓劃片右邊的坐標和底座對齊,這樣保證劃片不越界。當手指松開后,判斷劃片的中線坐標是在底座兒中線坐標的左邊還是右邊,以此來決定劃片最終是停在左邊還是右邊。同時改變開關的狀態,將開關的狀態回調給控件的調用中,讀取開關的狀態。

代碼實現。 主程序(調用者)中的代碼:

package com.example.aswitch;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.widget.Toast;public class MainActivity extends AppCompatActivity {  private MyToggleButton toggleButton;  @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);    toggleButton = (MyToggleButton) findViewById(R.id.toggle_button);    toggleButton.setOnStateChangedListener(new MyToggleButton.OnStateChangedListener() {      @Override      public void onStateChanged(boolean state) {        Toast.makeText(MainActivity.this, state ? "開" : "關", Toast.LENGTH_SHORT).show();      }    });  }}

自定義開關的具體實現;

package com.example.aswitch;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;/** * Created by huang on 2016/12/1. */public class MyToggleButton extends View {  private Bitmap background;  private Bitmap slideIcon;  private boolean state;  private OnStateChangedListener mOnStateChangedListener;  private int backgroundWidth;  private int backgroundHeight;  private int slideIconWidth;  private int slideIconHeight;  private int slideIconLeft;  private int maxSlideIconLeft;  public MyToggleButton(Context context, AttributeSet attrs) {    super(context, attrs);    String namespace = "http://schemas.android.com/apk/res-auto";    int slideBackgroundResId = attrs.getAttributeResourceValue(namespace, "slideBackground", -1);    int slideIconResId = attrs.getAttributeResourceValue(namespace, "slideIcon", -1);    if (slideBackgroundResId != -1 && slideIconResId != -1) {      setSwitchImage(slideBackgroundResId, slideIconResId);    }    boolean state = attrs.getAttributeBooleanValue(namespace, "state", false);    setState(state);  }  /**   * 設置開關的圖片   * @param slideBackgroundResId 開關的背景圖片資源id   * @param slideIconResId 開關上面的滑塊icon   */  public void setSwitchImage(int slideBackgroundResId, int slideIconResId) {    background = BitmapFactory.decodeResource(getResources(), slideBackgroundResId);    slideIcon = BitmapFactory.decodeResource(getResources(), slideIconResId);    backgroundWidth = background.getWidth();    backgroundHeight = background.getHeight();    slideIconWidth = slideIcon.getWidth();    slideIconHeight = slideIcon.getHeight();    maxSlideIconLeft = backgroundWidth - slideIconWidth;  }  /** 設置開關按鈕的狀態 */  public void setState(boolean state) {    checkState(state);    if (state) {      slideIconLeft = maxSlideIconLeft;    } else {      slideIconLeft = 0;    }  }  public void setOnStateChangedListener(OnStateChangedListener mOnStateChangedListener) {    this.mOnStateChangedListener = mOnStateChangedListener;  }  /** 開關按鈕狀態改變的監聽器 */  public interface OnStateChangedListener {    void onStateChanged(boolean state);  }  /**   * 對View進行測量的方法   */  @Override  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    setMeasuredDimension(backgroundWidth, backgroundHeight);  }  /** 把View畫出來的方法   * @param canvas 畫布   * */  @Override  protected void onDraw(Canvas canvas) {    int left = 0;    int top = 0;    canvas.drawBitmap(background, left, top, null);    canvas.drawBitmap(slideIcon, slideIconLeft, 0, null);  }  @Override  public boolean onTouchEvent(MotionEvent event) {    switch (event.getAction()) {      case MotionEvent.ACTION_DOWN:      case MotionEvent.ACTION_MOVE:        slideIconLeft = (int) (event.getX() - slideIconWidth / 2);        if (slideIconLeft < 0) {          slideIconLeft = 0;        } else if (slideIconLeft > maxSlideIconLeft) {          slideIconLeft = maxSlideIconLeft;        }        break;      case MotionEvent.ACTION_UP:        if (event.getX() < backgroundWidth / 2) {          slideIconLeft = 0;          checkState(false);        } else {          slideIconLeft = maxSlideIconLeft;          checkState(true);        }        break;    }    invalidate();    return true;  }  private void checkState(boolean state) {    if (this.state != state) {      this.state = state;      if (mOnStateChangedListener != null) {        mOnStateChangedListener.onStateChanged(state);      }    }  }}

布局文件的編寫:

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  xmlns:huang="http://schemas.android.com/apk/res-auto"  android:layout_width="match_parent"  android:layout_height="match_parent">  <com.example.aswitch.MyToggleButton    android:id="@+id/toggle_button"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    huang:slideBackground="@mipmap/slide_background2"    huang:slideIcon="@mipmap/slide_icon2"    huang:state="false" /></RelativeLayout>

為了使布局文件中的屬性有作用,還要單獨在values文件夾中寫一個attrs.xml文件,聲明相關的屬性。

<?xml version="1.0" encoding="utf-8"?><resources>  <declare-styleable name="MyToggleButton">    <attr name="slideBackground" format="reference" />    <attr name="slideIcon" format="reference" />    <attr name="state" format="boolean" />  </declare-styleable></resources> 

案例五 下拉刷新的Listview:

功能介紹: 系統本身的Listview默認情況下是沒有下拉刷新和上拉加載更多的功能。但是Listview有addHeadview和addFootView方法,因此可以為listview加頭布局或者腳布局。對于頭布局來說,有三個狀態,下拉刷新、松開刷新、正在刷新,同時伴有相應的動畫予以提示。刷新成功后,為listview添加一套信息。同樣的腳布局加載成功后,在listview的最后一條數據后面,在加載一條信息。

實現步驟:

         1. 寫布局文件。這里除了在主界面中需要一個Listview外,還需要一個頭部的布局文件和一個尾部的布局文件。初始狀態時,頭布局和腳布局均是隱藏的。當滑動事件出發的時候,調整頭布局和腳布局的位置。這里。頭布局和腳布局的根表簽最好是使用Linearlayout進行包裹,否則在滑動顯示是容易出現問題。
         2. 寫一個類繼承listview。初始化界面布局,通過View.measure(0, 0)方法主動觸發測量,mesure內部會調用onMeasure,從而獲取到頭布局的高度。如步驟一所說,開始狀態時,頭布局是隱藏的,所以為頭布局設置panding值,來使得頭布局隱藏,只需要把setpadding中的第二個參數設置為負的頭文件的高。同樣的,顯示頭布局只需要把setpadding中的第二個參數設置為0。對于根布局的顯示和隱藏同樣采用上述的方法。當然也可以改變setpadding的第四個參數來控制顯示和隱藏。
         3. 重寫OntouchEvent()方法。手指按下時,記錄下初始位置。手指滑動時在記錄一個y坐標。通過兩次坐標的差值,判斷手指滑動的方向。在可見的第一個條目的position為0的時候,如果手指向下滑動,有兩種狀態,一個是下拉刷新,指的是頭布局從不可見到剛好全部可見,一個是松開刷新,頭布局全部可見后繼續向下拖動,就會進入松開刷新狀態。另外還有一種狀態就是正在刷新,這種狀態下,頭布局剛好停留在頂部,維持一段時間。實際開發中,正在刷新是向服務器請求網絡。這里添加的是假數據。不同的狀態之間的轉換還伴隨有動畫效果,因此,這里還寫有補間動畫來實現向上和向下的效果。
         4. 寫一個滑動監聽事件,在監聽事件中進行腳布局邏輯的編寫。當滿足以下三種情況的時候則顯示腳布局,一個ListView處于空閑狀態,另一個是界面上可見的最后一條item是ListView中最后的一條item,還有一個是如果當前沒有去做正在加載更多的事情。顯示腳布局的時候,不僅要設置padding值,同時選中listview最后一個條目,這樣就可以在界面上完整的顯示。
         5. 最后要注意的是,無論是同布局,還是腳布局,都要對狀態進行修改。所以,可以增加一個回調監聽。表明數據刷新或者加載完成,可以隱藏頭布局和腳布局了,同時,頭布局的狀態恢復為下拉刷新,腳布局不是正在加載。同時,停掉相關的動畫,修改顯示的狀態。

主程序(調用者)的編寫。一般刷新是請求數據庫數據,這里直接給出模擬數據演示效果。

package com.example.pulltofreshlistview;import android.os.Bundle;import android.os.Handler;import android.support.v7.app.AppCompatActivity;import android.widget.ArrayAdapter;import com.example.pulltofreshlistview.view.PullToRefreshListView;import java.util.ArrayList;public class MainActivity extends AppCompatActivity {  private PullToRefreshListView listView;  private ArrayList<String> datas;  private ArrayAdapter<String> adapter;  @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);    listView = (PullToRefreshListView) findViewById(R.id.list_view);    datas = new ArrayList<String>();    for (int i = 0; i < 20; i++) {      datas.add("我又撿到錢了,好開心啊^_^ /t" + i);    }    adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, datas);    listView.setAdapter(adapter);    listView.setOnRefreshingListener(new PullToRefreshListView.OnRefreshingListener() {      @Override      public void onRefreshing() {        reloadData();      }      @Override      public void onLoadMore() {        loadMore();      }    });  }  /**   * 重新聯網獲取數據   */  protected void reloadData() {    new Handler().postDelayed(new Runnable() {      @Override      public void run() {        datas.add(0, "我是刷新出來的數據");        adapter.notifyDataSetChanged();        listView.onRefreshComplete();      }    }, 3000);  }  /**   * 聯網加載更多數據   */  protected void loadMore() {    new Handler().postDelayed(new Runnable() {      @Override      public void run() {        datas.add("我是加載更多出來的數據");        adapter.notifyDataSetChanged();        listView.onLoadmoreComplete();      }    }, 3000);  }}     

下拉刷新listview的邏輯部分

package com.example.pulltofreshlistview.view;import android.content.Context;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;import android.view.animation.RotateAnimation;import android.widget.AbsListView;import android.widget.ImageView;import android.widget.ListView;import android.widget.ProgressBar;import android.widget.TextView;import com.example.pulltofreshlistview.R;/** * Created by huang on 2016/12/1. */public class PullToRefreshListView extends ListView {  private View headerView;  private float downY;  private int headerViewHeight;  private static final int STATE_PULL_TO_REFRESH = 0;  private static final int STATE_RELEASE_REFRESH = 1;  private static final int STATE_REFRESHING = 2;  private int currentState = STATE_PULL_TO_REFRESH;  // 默認是下拉刷新狀態  private ImageView iv_arrow;  private ProgressBar progress_bar;  private TextView tv_state;  private RotateAnimation upAnim;  private RotateAnimation downAnim;  private OnRefreshingListener mOnRefreshingListener;  private View footerView;  private int footerViewHeight;  private boolean loadingMore;  public PullToRefreshListView(Context context, AttributeSet attrs) {    super(context, attrs);    initHeaderView();    initFooterView();  }  private void initHeaderView() {    headerView = View.inflate(getContext(), R.layout.header_view, null);    iv_arrow = (ImageView) headerView.findViewById(R.id.iv_arrow);    progress_bar = (ProgressBar) headerView.findViewById(R.id.progress_bar);    showRefreshingProgressBar(false);    tv_state = (TextView) headerView.findViewById(R.id.tv_state);    headerView.measure(0, 0);    headerViewHeight = headerView.getMeasuredHeight();    hideHeaderView();    super.addHeaderView(headerView);    upAnim = createRotateAnim(0f, -180f);    downAnim = createRotateAnim(-180f, -360f);  }  private void initFooterView() {    footerView = View.inflate(getContext(), R.layout.footer_view, null);    footerView.measure(0, 0);    footerViewHeight = footerView.getMeasuredHeight();    hideFooterView();    super.addFooterView(footerView);    super.setOnScrollListener(new OnScrollListener() {      @Override      public void onScrollStateChanged(AbsListView view, int scrollState) {        if (scrollState == OnScrollListener.SCROLL_STATE_IDLE            && getLastVisiblePosition() == getCount() - 1            && loadingMore == false            ) {          loadingMore = true;          showFooterView();          setSelection(getCount() - 1);          if (mOnRefreshingListener != null) {            mOnRefreshingListener.onLoadMore();          }        }      }      @Override      public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {      }    });  }  private void hideFooterView() {    int paddingTop = -footerViewHeight;    setFooterViewPaddingTop(paddingTop);  }  private void showFooterView() {    int paddingTop = 0;    setFooterViewPaddingTop(paddingTop);  }  private void setFooterViewPaddingTop(int paddingTop) {    footerView.setPadding(0, paddingTop, 0, 0);  }  /**   * 設置顯示進度的圈圈   *   * @param showProgressBar 如果是true,則顯示ProgressBar,否則的話顯示箭頭   */  private void showRefreshingProgressBar(boolean showProgressBar) {    progress_bar.setVisibility(showProgressBar ? View.VISIBLE : View.GONE);    iv_arrow.setVisibility(!showProgressBar ? View.VISIBLE : View.GONE);    if (showProgressBar) {      iv_arrow.clearAnimation();  // 有動畫的View要清除動畫才能真正的隱藏    }  }  /**   * 創建旋轉動畫   *   * @param fromDegrees 從哪個角度開始轉   * @param toDegrees  轉到哪個角度   * @return   */  private RotateAnimation createRotateAnim(float fromDegrees, float toDegrees) {    int pivotXType = RotateAnimation.RELATIVE_TO_SELF;    int pivotYType = RotateAnimation.RELATIVE_TO_SELF;    float pivotXValue = 0.5f;    float pivotYValue = 0.5f;    RotateAnimation ra = new RotateAnimation(fromDegrees, toDegrees, pivotXType, pivotXValue, pivotYType, pivotYValue);    ra.setDuration(300);    ra.setFillAfter(true);    return ra;  }  /**   * 隱藏HeaderView   */  private void hideHeaderView() {    int paddingTop = -headerViewHeight;    setHeaderViewPaddingTop(paddingTop);  }  /**   * 顯示HeaderView   */  private void showHeaderView() {    int paddingTop = 0;    setHeaderViewPaddingTop(paddingTop);  }  /**   * 設置HeaderView的paddingTop   *   * @param paddingTop   */  private void setHeaderViewPaddingTop(int paddingTop) {    headerView.setPadding(0, paddingTop, 0, 0);  }  @Override  public boolean onTouchEvent(MotionEvent ev) {    switch (ev.getAction()) {      case MotionEvent.ACTION_DOWN:        downY = ev.getY();        break;      case MotionEvent.ACTION_MOVE:        if (currentState == STATE_REFRESHING) {          return super.onTouchEvent(ev);        }        int fingerMoveDistanceY = (int) (ev.getY() - downY);    // 手指移動的距離        if (fingerMoveDistanceY > 0 && getFirstVisiblePosition() == 0) {          int paddingTop = -headerViewHeight + fingerMoveDistanceY;          setHeaderViewPaddingTop(paddingTop);          if (paddingTop < 0 && currentState != STATE_PULL_TO_REFRESH) {            currentState = STATE_PULL_TO_REFRESH;            tv_state.setText("下拉刷新");            iv_arrow.startAnimation(downAnim);            showRefreshingProgressBar(false);          } else if (paddingTop >= 0 && currentState != STATE_RELEASE_REFRESH) {            currentState = STATE_RELEASE_REFRESH;            tv_state.setText("松開刷新");            iv_arrow.startAnimation(upAnim);            showRefreshingProgressBar(false);          }          return true;        }        break;      case MotionEvent.ACTION_UP:        if (currentState == STATE_RELEASE_REFRESH) {          currentState = STATE_REFRESHING;          tv_state.setText("正在刷新");          showRefreshingProgressBar(true);          showHeaderView();          if (mOnRefreshingListener != null) {            mOnRefreshingListener.onRefreshing();          }        } else if (currentState == STATE_PULL_TO_REFRESH) {          hideHeaderView();        }        break;    }    return super.onTouchEvent(ev);  }  public void setOnRefreshingListener(OnRefreshingListener mOnRefreshingListener) {    this.mOnRefreshingListener = mOnRefreshingListener;  }  /**   * ListView刷新的監聽器   */  public interface OnRefreshingListener {    void onRefreshing();    void onLoadMore();  }  /**   * 聯網刷新數據的操作已經完成了   */  public void onRefreshComplete() {    hideHeaderView();    currentState = STATE_PULL_TO_REFRESH;    showRefreshingProgressBar(false);  }  /**   * 加載更多新數據的操作已經完成了   */  public void onLoadmoreComplete() {    hideFooterView();    loadingMore = false;  }}

布局文件包含三個部分,listview主體部分:

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  xmlns:tools="http://schemas.android.com/tools"  android:layout_width="match_parent"  android:layout_height="match_parent"  tools:context=".MainActivity">  <com.example.pulltofreshlistview.view.PullToRefreshListView    android:id="@+id/list_view"    android:layout_width="match_parent"    android:layout_height="match_parent" /></RelativeLayout>

頭布局:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  android:layout_width="match_parent"  android:layout_height="wrap_content"  android:orientation="horizontal"  android:gravity="center_vertical" >  <RelativeLayout    android:layout_width="50dp"    android:layout_height="50dp">    <ImageView      android:id="@+id/iv_arrow"      android:layout_width="wrap_content"      android:layout_height="wrap_content"      android:src="@mipmap/arrow"      android:layout_centerInParent="true"/>    <ProgressBar      android:id="@+id/progress_bar"      style="@android:style/Widget.ProgressBar"      android:indeterminateDrawable="@drawable/progress_medium_red"      android:layout_width="28dp"      android:layout_height="28dp"      android:layout_centerInParent="true"      android:visibility="gone"/>  </RelativeLayout>  <LinearLayout    android:layout_width="match_parent"    android:layout_height="wrap_content"    android:gravity="center"    android:orientation="vertical">    <TextView      android:id="@+id/tv_state"      android:layout_width="wrap_content"      android:layout_height="wrap_content"      android:textColor="#FF0000"      android:text="下拉刷新"/>    <TextView      android:layout_width="wrap_content"      android:layout_height="wrap_content"      android:textColor="#666666"      android:textSize="12sp"      android:text="最后刷新時間:2015-07-25 19:59:39"      android:layout_marginTop="4dp"/>  </LinearLayout></LinearLayout>

腳布局:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  android:layout_width="match_parent"  android:layout_height="wrap_content"  android:gravity="center"  android:orientation="horizontal">  <ProgressBar    style="@android:style/Widget.ProgressBar"    android:layout_width="28dp"    android:layout_height="28dp"    android:layout_marginBottom="8dp"    android:layout_marginTop="8dp"    android:indeterminateDrawable="@drawable/progress_medium_red" />  <TextView    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:layout_marginLeft="6dp"    android:text="加載更多..."    android:textColor="#FF0000" /></LinearLayout>

案例六 側滑菜單:

功能分析: 之前的案例大部分都只有一個控件,因此是通過繼承view實現。側滑菜單顯然需要兩個控件才能實現,一個用于加載主界面,一個用于加載側滑界面。所以這里需要繼承viewgroup。通過在主界面滑動來控制界面的顯示和隱藏。

實現步驟: 

          1. 布局文件的書寫。布局文件包含兩個部分,主界面和側滑界面。主界面中有一個標題欄,標題欄有圖片按鈕,文字組成。圖片按牛同樣可以控制側邊欄的顯示和隱藏。側邊欄在ScrollView中添加一個Linearlayout,在linearlayout中縱向排列textview顯示文本。當然,側滑菜單同樣可以用listview顯示,這里為簡單起見就不采用listview顯示。此外,TextView要想響應點擊事件,需要設置clickable為true。
          2. 創建一個類繼承于ViewGroup。重寫onmeasure()方法,測量測量控件大小,包括兩個子控件。這里,側滑菜單的寬在布局文件中寫好了,主界面的寬適配手機界面。所以分別調用menu.measure(menuWidth, heightMeasureSpec)和main.measure(widthMeasureSpec, heightMeasureSpec)方法即可獲取。排版容器中的子View。由于該自定義的控件包含不止一個子view,所以重寫onlayout()方法是必不可少的。對子View進行排版,子View的0,0坐標是SlidingMenu的左上角。對側滑菜單進行排版,菜單的left坐標在負的菜單寬的位置,菜單的top坐標在0的位置,菜單的right坐標在0的位置,菜單的bottom坐標在容器的最底邊。對主界面進行排版,主界面的left坐標在0的位置,主界面的top坐標在0的位置,主界面的right坐標在容器的最右邊,主界面的bottom坐標在容器的最底邊。
          3. 重寫onInterceptTouchEvent。本案例中只關心水平劃動,所以如果水平移動距離比垂直移動距離大,則認為是水平移動,把事件攔截,不讓ScrollView使用,此事件交由控件本身處理。這里有必要介紹一下事件分發。視圖集合對于事件的分發,自上而下處理。ViewGroup擁有下面3個Touch相關方法,dispatchTouchEvent(MotionEvent ev)用于Touch事件的頒發,onInterceptTouchEvent(MotionEvent ev) 用于攔截Touch事件,onTouchEvent(MotionEvent event) 用于處理Touch事件。這里重寫了onInterceptTouchEvent(),相應的還要重寫ontouchevent()方法。
          4. 在ontouchevent()方法中實現滑動跟隨的邏輯。滑動事件中,只關心橫向滑動。按下時,記錄下當前的x坐標。滑動時,再次記錄當前x坐標,兩者相減得到移動的距離。通過scrollTo()方法滑到相應的位置。系統給定的scrollTo()方法默認向右為負值,所以可重寫scrollTo()方法保證移動為正值。代碼優化。其實上述過程可以實現側滑菜單的功能。當滑動過程略顯生硬。這時就可以用到Scroller,這個類專門用于模擬滾動的數值。同時要配合computeScroll()方法使用。
          5. 還有一個菜單的開關按鈕,要么開,要么關。開的時候完全顯示側滑菜單。關的時候,隱藏側滑菜單。這點邏輯和ontouchevent()中手指抬起的邏輯一樣。

主程序(調用者)的編寫:

package com.example.slidingmenu;import android.app.Activity;import android.os.Bundle;import android.view.View;import android.view.Window;import android.widget.LinearLayout;import android.widget.TextView;import android.widget.Toast;public class MainActivity extends Activity {  private SlidingMenu sliding_menu;  private LinearLayout ll_menu;  private TextView tv_news;  @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    requestWindowFeature(Window.FEATURE_NO_TITLE);    setContentView(R.layout.activity_main);    sliding_menu = (SlidingMenu) findViewById(R.id.sliding_menu);    ll_menu = (LinearLayout) findViewById(R.id.ll_menu);    tv_news = (TextView) findViewById(R.id.tv_news);    setCurrentSelectedMenuItem(tv_news);  }  /** 設置當前選擇的菜單Item */  private void setCurrentSelectedMenuItem(View menuItem) {    for (int i = 0; i < ll_menu.getChildCount(); i++) {      View child = ll_menu.getChildAt(i);      child.setSelected(child == menuItem);    }  }  /** 菜單列表中的某個菜單項被單擊了 */  public void onMenuItemClick(View v) {    TextView textView = (TextView) v;    Toast.makeText(this, textView.getText(), Toast.LENGTH_SHORT).show();    setCurrentSelectedMenuItem(v);  }  /** 主界面上的菜單按鈕被單擊了 */  public void onMenuToggleClick(View v) {    sliding_menu.toggle();  }}

側滑菜單的主體邏輯。

package com.example.slidingmenu;import android.content.Context;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;import android.view.ViewGroup;import android.widget.Scroller;/** * Created by huang on 2016/12/1. */public class SlidingMenu extends ViewGroup {  private View menu;  private View main;  private int menuWidth;  private int downX;  private int currentX;  /** 這個類專門用于模擬滾動的數值 */  private Scroller scroller;  public SlidingMenu(Context context, AttributeSet attrs) {    super(context, attrs);    scroller = new Scroller(context);  }  @Override  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    super.onMeasure(widthMeasureSpec, heightMeasureSpec);  // 測量容器自己的寬高    menu = getChildAt(0); // 獲取菜單容器    main = getChildAt(1); // 獲取主界面容器    menuWidth = menu.getLayoutParams().width;   // 獲取菜單的寬    // 測量菜單    menu.measure(menuWidth, heightMeasureSpec);    // 測量主界面    main.measure(widthMeasureSpec, heightMeasureSpec);  }  @Override  protected void onLayout(boolean changed, int l, int t, int r, int b) {    int menuLeft = -menuWidth;    int menuTop = 0;    int menuRight = 0;    int menuBottom = b - t;    menu.layout(menuLeft, menuTop, menuRight, menuBottom);    int mainLeft = 0;    int mainTop = 0;    int mainRight = r - l;    int mainBottom = b - t;    main.layout(mainLeft, mainTop, mainRight, mainBottom);  }  /**   * 讓界面滾動到x的位置,傳正數往右移,傳負往左移   * @param x   */  public void scrollTo(int x) {    super.scrollTo(-x, 0);  }  /** 獲取當前滑動到的位置 */  public int getMyScrollX() {    return -super.getScrollX();  }  @Override  public boolean onInterceptTouchEvent(MotionEvent ev) {    switch (ev.getAction()) {      case MotionEvent.ACTION_DOWN:        downY = (int) ev.getY();        downX = (int) ev.getX();        break;      case MotionEvent.ACTION_MOVE:        int distanceX = Math.abs((int) (ev.getX() - downX));        int distanceY = Math.abs((int) (ev.getY() - downY));        if (distanceX > distanceY) {          return true;        }        break;    }    return super.onInterceptTouchEvent(ev);  }  @Override  public boolean onTouchEvent(MotionEvent event) {    switch (event.getAction()) {      case MotionEvent.ACTION_DOWN:        downX = (int) event.getX();        break;      case MotionEvent.ACTION_MOVE:        int fingerMoveDistanceX = (int) event.getX() - downX;        int destX = currentX + fingerMoveDistanceX;        if (destX < 0) {          destX = 0;        } else if (destX > menuWidth){          destX = menuWidth;        }        scrollTo(destX);        break;      case MotionEvent.ACTION_UP:        if (getMyScrollX() < menuWidth / 2) {          startScroll(0);        } else {          startScroll(menuWidth);        }        break;    }    return true;  }  int count;  private int downY;  /**   * 以動畫的方式滾動到指定的位置   *   * @param destX 要滑動到哪里(目標位置)   */  private void startScroll(int destX) {    currentX = destX;    int startX = getMyScrollX();    int distatnceX = destX - startX;    int duration = 800;    scroller.startScroll(startX, 0, distatnceX, 0, duration);    invalidate();  }  @Override  public void computeScroll() {    if (scroller.computeScrollOffset()) {      int currX = scroller.getCurrX();      scrollTo(currX);      invalidate();      count++;    }    System.out.println("count = " + count);  }  /** 菜單的開關按鈕,要么開,要么關 */  public void toggle() {    if (getMyScrollX() > 0) {      startScroll(0);    } else {      startScroll(menuWidth);    }  }}

 整體布局

<?xml version="1.0" encoding="utf-8"?><com.example.slidingmenu.SlidingMenu xmlns:android="http://schemas.android.com/apk/res/android"  android:id="@+id/sliding_menu"  android:layout_width="match_parent"  android:layout_height="match_parent">  <!-- 在SlidingMenu中的索引0 -->  <include layout="@layout/menu" />  <!-- 在SlidingMenu中的索引1 -->  <include layout="@layout/main" /></com.example.slidingmenu.SlidingMenu>

主界面布局

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  android:layout_width="match_parent"  android:layout_height="match_parent"  android:orientation="vertical">  <LinearLayout    android:layout_width="match_parent"    android:layout_height="wrap_content"    android:background="@mipmap/top_bar_bg"    android:gravity="center_vertical"    android:orientation="horizontal">    <ImageView      android:layout_width="wrap_content"      android:layout_height="wrap_content"      android:onClick="onMenuToggleClick"      android:src="@mipmap/main_back" />    <View      android:layout_width="1dp"      android:layout_height="match_parent"      android:layout_marginBottom="6dp"      android:layout_marginTop="6dp"      android:background="@mipmap/top_bar_divider" />    <TextView      android:layout_width="wrap_content"      android:layout_height="wrap_content"      android:layout_marginLeft="12dp"      android:text="新聞"      android:textColor="@android:color/white"      android:textSize="34sp" />  </LinearLayout>  <TextView    android:layout_width="match_parent"    android:layout_height="match_parent"    android:gravity="center"    android:text="為了一個小饅頭,友誼的小船說翻就翻"    android:textSize="30sp" /></LinearLayout>

側滑界面布局

<?xml version="1.0" encoding="utf-8"?><ScrollView xmlns:android="http://schemas.android.com/apk/res/android"  android:layout_width="240dp"  android:layout_height="match_parent"  android:background="@mipmap/menu_bg">  <LinearLayout    android:id="@+id/ll_menu"    android:layout_width="240dp"    android:layout_height="wrap_content"    android:orientation="vertical" >    <TextView      android:id="@+id/tv_news"      android:text="新聞"      style="@style/menu_item"      android:drawableLeft="@mipmap/tab_news"/>    <TextView      android:text="訂閱"      style="@style/menu_item"      android:drawableLeft="@mipmap/tab_read"/>    <TextView      android:text="本地"      style="@style/menu_item"      android:drawableLeft="@mipmap/tab_local"/>    <TextView      android:text="跟貼"      style="@style/menu_item"      android:drawableLeft="@mipmap/tab_ties"/>    <TextView      android:text="圖片"      style="@style/menu_item"      android:drawableLeft="@mipmap/tab_pics"/>    <TextView      android:text="話題"      style="@style/menu_item"      android:drawableLeft="@mipmap/tab_ugc"/>    <TextView      android:text="投票"      style="@style/menu_item"      android:drawableLeft="@mipmap/tab_vote"/>    <TextView  android:text="聚合閱讀"  style="@style/menu_item"  android:drawableLeft="@mipmap/tab_focus"/>  </LinearLayout>  </ScrollView>

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 清水县| 香港 | 丁青县| SHOW| 深圳市| 广州市| 黄山市| 枣庄市| 邵阳县| 灵石县| 阳东县| 田阳县| 萨迦县| 长寿区| 灵宝市| 枣庄市| 于田县| 泸溪县| 新巴尔虎右旗| 南投市| 日照市| 策勒县| 泰宁县| 蒙山县| 龙胜| 明溪县| 新安县| 辽源市| 枣阳市| 临高县| 化隆| 吉林省| 盘锦市| 哈尔滨市| 惠州市| 枝江市| 和田市| 阿拉善左旗| 分宜县| 芜湖县| 健康|