欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

 更新時間:2016年12月06日 10:03:07   作者:huang502  
這篇文章主要介紹了Android自定義控件案例匯總,自定義開關(guān)、Listview實(shí)現(xiàn)下拉刷新、側(cè)滑菜單,具有一定的參考價值,感興趣的小伙伴們可以參考一下

案例四 自定義開關(guān):

功能介紹:本案例實(shí)現(xiàn)的功能是創(chuàng)建一個自定義的開關(guān),可以自行決定開關(guān)的背景。當(dāng)滑動開關(guān)時,開關(guān)的滑塊可跟隨手指移動。當(dāng)手指松開后,滑塊根據(jù)開關(guān)的狀態(tài),滑到最右邊或者滑到最左邊,同時保存開關(guān)的狀態(tài),將開關(guān)的狀態(tài)回調(diào)給調(diào)用者。當(dāng)然,上述功能系統(tǒng)給定的switch控件也可以實(shí)現(xiàn)。

實(shí)現(xiàn)步驟:

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

代碼實(shí)現(xiàn)。 主程序(調(diào)用者)中的代碼:

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 ? "開" : "關(guān)", Toast.LENGTH_SHORT).show();
      }
    });
  }
}


自定義開關(guān)的具體實(shí)現(xiàn);

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);
  }

  /**
   * 設(shè)置開關(guān)的圖片
   * @param slideBackgroundResId 開關(guān)的背景圖片資源id
   * @param slideIconResId 開關(guān)上面的滑塊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;
  }

  /** 設(shè)置開關(guān)按鈕的狀態(tài) */
  public void setState(boolean state) {
    checkState(state);
    if (state) {
      slideIconLeft = maxSlideIconLeft;
    } else {
      slideIconLeft = 0;
    }
  }

  public void setOnStateChangedListener(OnStateChangedListener mOnStateChangedListener) {
    this.mOnStateChangedListener = mOnStateChangedListener;
  }

  /** 開關(guān)按鈕狀態(tài)改變的監(jiān)聽器 */
  public interface OnStateChangedListener {
    void onStateChanged(boolean state);
  }

  /**
   * 對View進(jìn)行測量的方法
   */
  @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>


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

<?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:

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

實(shí)現(xiàn)步驟:

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

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

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();
      }
    });
  }

  /**
   * 重新聯(lián)網(wǎng)獲取數(shù)據(jù)
   */
  protected void reloadData() {
    new Handler().postDelayed(new Runnable() {

      @Override
      public void run() {
        datas.add(0, "我是刷新出來的數(shù)據(jù)");
        adapter.notifyDataSetChanged();
        listView.onRefreshComplete();
      }
    }, 3000);
  }

  /**
   * 聯(lián)網(wǎng)加載更多數(shù)據(jù)
   */
  protected void loadMore() {
    new Handler().postDelayed(new Runnable() {

      @Override
      public void run() {
        datas.add("我是加載更多出來的數(shù)據(jù)");
        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;  // 默認(rèn)是下拉刷新狀態(tài)
  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);
  }

  /**
   * 設(shè)置顯示進(jìn)度的圈圈
   *
   * @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要清除動畫才能真正的隱藏
    }
  }

  /**
   * 創(chuàng)建旋轉(zhuǎn)動畫
   *
   * @param fromDegrees 從哪個角度開始轉(zhuǎn)
   * @param toDegrees  轉(zhuǎn)到哪個角度
   * @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);
  }

  /**
   * 設(shè)置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刷新的監(jiān)聽器
   */
  public interface OnRefreshingListener {
    void onRefreshing();

    void onLoadMore();
  }

  /**
   * 聯(lián)網(wǎng)刷新數(shù)據(jù)的操作已經(jīng)完成了
   */
  public void onRefreshComplete() {
    hideHeaderView();
    currentState = STATE_PULL_TO_REFRESH;
    showRefreshingProgressBar(false);
  }

  /**
   * 加載更多新數(shù)據(jù)的操作已經(jīng)完成了
   */
  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>


案例六 側(cè)滑菜單:

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

實(shí)現(xiàn)步驟: 

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

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

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);
  }

  /** 設(shè)置當(dāng)前選擇的菜單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);
    }
  }

  /** 菜單列表中的某個菜單項(xiàng)被單擊了 */
  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();
  }

}


側(cè)滑菜單的主體邏輯。

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;
  /** 這個類專門用于模擬滾動的數(shù)值 */
  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的位置,傳正數(shù)往右移,傳負(fù)往左移
   * @param x
   */
  public void scrollTo(int x) {
    super.scrollTo(-x, 0);
  }

  /** 獲取當(dāng)前滑動到的位置 */
  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 要滑動到哪里(目標(biāo)位置)
   */
  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);
  }

  /** 菜單的開關(guān)按鈕,要么開,要么關(guān) */
  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>


側(cè)滑界面布局

<?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>


以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Android框架學(xué)習(xí)之Volley和Glide詳解

    Android框架學(xué)習(xí)之Volley和Glide詳解

    這篇文章主要給大家介紹了關(guān)于Android框架學(xué)習(xí)之Volley和Glide的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對各位Android開發(fā)者們具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-05-05
  • android模擬實(shí)現(xiàn)航拍遙控

    android模擬實(shí)現(xiàn)航拍遙控

    這篇文章主要為大家詳細(xì)介紹了android模擬實(shí)現(xiàn)航拍遙控,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • 一個吸頂Item的簡單實(shí)現(xiàn)方法分享

    一個吸頂Item的簡單實(shí)現(xiàn)方法分享

    這篇文章主要給大家介紹了一個吸頂Item的簡單實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對各位Android開發(fā)者們具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • Android permission denied原因歸納和解決辦法

    Android permission denied原因歸納和解決辦法

    大家好,本篇文章主要講的是Android permission denied原因歸納和解決辦法,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下哦
    2021-12-12
  • Android實(shí)戰(zhàn)APP啟動速度優(yōu)化

    Android實(shí)戰(zhàn)APP啟動速度優(yōu)化

    本篇文章給大家通過實(shí)戰(zhàn)總結(jié)了Android開發(fā)APP啟動速度優(yōu)化的方法以及需要注意的地方,有需要的朋友可以參考下。
    2018-05-05
  • android表格效果之ListView隔行變色實(shí)現(xiàn)代碼

    android表格效果之ListView隔行變色實(shí)現(xiàn)代碼

    首先繼承SimpleAdapter再使用重載的Adapter來達(dá)到效果,其實(shí)主要是需要重載SimpleAdapter,感興趣的朋友可以研究下,希望本文可以幫助到你
    2013-02-02
  • OpenGL Shader實(shí)例分析(4)閃光效果

    OpenGL Shader實(shí)例分析(4)閃光效果

    這篇文章主要為大家詳細(xì)介紹了OpenGL Shader實(shí)例分析第4篇,實(shí)現(xiàn)閃光效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-02-02
  • Android實(shí)現(xiàn)裁剪照片功能

    Android實(shí)現(xiàn)裁剪照片功能

    這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)裁剪照片功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • Android BottomSheet效果的兩種實(shí)現(xiàn)方式

    Android BottomSheet效果的兩種實(shí)現(xiàn)方式

    這篇文章主要介紹了Android BottomSheet效果的兩種實(shí)現(xiàn)方式,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-08-08
  • Android使用ViewPager完成app引導(dǎo)頁

    Android使用ViewPager完成app引導(dǎo)頁

    這篇文章主要為大家詳細(xì)介紹了Android使用ViewPager完成app引導(dǎo)頁,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-11-11

最新評論