麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁(yè) > 系統(tǒng) > Android > 正文

Android左右滑出菜單實(shí)例分析

2020-04-11 12:16:05
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友
現(xiàn)在的Android應(yīng)用,只要有一個(gè)什么新的創(chuàng)意,過(guò)不了多久,幾乎所有的應(yīng)用都帶這個(gè)創(chuàng)意。這不,咱們公司最近的一個(gè)持續(xù)性的項(xiàng)目,想在首頁(yè)加個(gè)從左滑動(dòng)出來(lái)的菜單,我查閱網(wǎng)上資料,并自己摸索,實(shí)現(xiàn)了左、右兩邊都能滑出菜單,并且,左、右菜單中,都可以加ListView等這類需要解決GestureDetector沖突的問(wèn)題(如在首頁(yè)面中,含有ListView,上下滾動(dòng)時(shí),左右不動(dòng),相反,左右滑動(dòng)菜單時(shí),上下不動(dòng),聽著頭就大了吧!)

先上幾張圖,給大家瞧瞧,對(duì)整體有個(gè)了解:
 
一、首頁(yè)布局:
復(fù)制代碼 代碼如下:

<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" >
<!-- 主布局 -->
<RelativeLayout
android:id="@+id/mainLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:id="@+id/titlebar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ffffff"
android:padding="5dip">
<ImageView
android:id="@+id/ivMore"
android:src="@drawable/nav_more_normal"
android:contentDescription="@string/img_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_alignParentLeft="true"
android:layout_marginLeft="10dip"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="@string/title"
android:textSize="20sp"
android:textColor="#000000"/>
<ImageView
android:id="@+id/ivSettings"
android:src="@drawable/nav_setting_normal"
android:contentDescription="@string/img_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_alignParentRight="true"
android:layout_marginRight="10dip"/>
</RelativeLayout>

<ImageView
android:src="@drawable/picture"
android:contentDescription="@string/img_desc"
android:scaleType="fitXY"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/titlebar"/>
</RelativeLayout>
<!-- 左側(cè)菜單導(dǎo)航 -->
<RelativeLayout
android:id="@+id/leftLayout"
android:layout_width="140dip"
android:layout_height="match_parent"
android:background="#000000">
<RelativeLayout
android:id="@+id/leftTitlebar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/grey21"
android:padding="5dip">
<TextView
android:layout_marginLeft="5dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:text="@string/leftNav"
android:textSize="20sp"
android:textColor="#ffffff"/>
</RelativeLayout>
<com.chris.lr.slidemenu.LayoutRelative
android:id="@+id/layoutSlideMenu"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/leftTitlebar">
<ListView
android:id="@+id/listMore"
android:cacheColorHint="#00000000"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</com.chris.lr.slidemenu.LayoutRelative>
</RelativeLayout>
<!-- 右側(cè)菜單導(dǎo)航 -->
<RelativeLayout
android:id="@+id/rightLayout"
android:layout_width="140dip"
android:layout_height="match_parent"
android:background="#000000">
<RelativeLayout
android:id="@+id/rightTitlebar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/gold"
android:padding="5dip">
<TextView
android:layout_marginLeft="5dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:text="@string/right_title"
android:textSize="20sp"
android:textColor="#ffffff"/>
</RelativeLayout>

<TextView
android:text="@string/rightNav"
android:textColor="#ff00ff"
android:textSize="18sp"
android:layout_width="match_parent"
android:layout_height="30dip"
android:layout_below="@id/rightTitlebar"
android:background="#000000"/>
</RelativeLayout>
</RelativeLayout>

布局很簡(jiǎn)單,我個(gè)人比較推薦用RelativeLayout,因?yàn)檫@個(gè)是幾個(gè)Layout中,性能最好的,而LinearLayout則不好,原因在于,某個(gè)子視圖的寬高變動(dòng),會(huì)引起這個(gè)布局中其它地方也需要重新調(diào)整。

布局中,有com.chris.lr.slidemenu.LayoutRelative這個(gè)自定義控件是繼承RelativeLayout的,里面只是加了些手勢(shì)的處理,它的作用實(shí)際上就是最開始講到的,如果含有ListView這類需要判斷手勢(shì)的,則就用到它,先由它來(lái)判斷,然后在視情況是否攔截由自己來(lái)處理。

二、自定義控件:
復(fù)制代碼 代碼如下:

package com.chris.lr.slidemenu;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.widget.RelativeLayout;
public class LayoutRelative extends RelativeLayout {
private static final String TAG = "ChrisSlideMenu";
private GestureDetector mGestureDetector;
private boolean bLockScrollX = false;
private boolean bTouchIntercept = false;

private OnScrollListener mOnScrollListenerCallback = null;

public LayoutRelative(Context context) {
this(context, null);
}

public LayoutRelative(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public LayoutRelative(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);

mGestureDetector = new GestureDetector(new LayoutGestureListener());
}

/**
* 設(shè)置滾動(dòng)監(jiān)聽接口
* @param l
*/
public void setOnScrollListener(OnScrollListener l){
mOnScrollListenerCallback = l;
}

/*
* Progress:
* 1. 重載dispatchTouchEvent,將消息傳遞給GestureDetector;
* 2. 重載手勢(shì)中onDown 和 onScroll兩個(gè)函數(shù);
* 3. 在onDown中,默認(rèn)對(duì)水平滾動(dòng)方向加鎖;
* 4. 在onScroll中,判斷e1與e2的水平方向與垂直方向距離:
* a. 如果垂直方向大,則表明是上下滾動(dòng),且返回false表明當(dāng)前手勢(shì)不用攔截;
* b. 如果水平方向大,則表明是左右滾動(dòng),且返回true表明當(dāng)前手勢(shì)需要攔截;
* 5. 重載onInterceptTouchEvent,如果手勢(shì)返回為true,則onInterceptTouchEvent也返回true;
* 6. 如果要攔截手勢(shì)消息,則需要重載onTouchEvent,或子視圖中重載這個(gè)函數(shù),來(lái)處理這條消息;
* 7. 如果自己處理,則對(duì)水平方向滾動(dòng)去鎖(表明當(dāng)前用戶想左右滾動(dòng));
*
* ----------
* ---------------------- ------>| onDown |
* | | | ----------
* | dispatchTouchEvent | <---- ------ false: 上下滾動(dòng)
* | | | ------------ /
* ---------------------- ------>| onScroll | ----
* | ------------ /
* | ------ true : 左右滾動(dòng)
* | intercept = true ----------------
* |----------------------| onTouchEvent |
* | ----------------
* -------------------------
* | |
* | onInterceptTouchEvent |
* | |
* -------------------------
*
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
bTouchIntercept = mGestureDetector.onTouchEvent(ev);

if(MotionEvent.ACTION_UP == ev.getAction() && !bLockScrollX){
if(mOnScrollListenerCallback != null){
mOnScrollListenerCallback.doOnRelease();
}
}

return super.dispatchTouchEvent(ev);
}

// viewgroup.onInterceptTouchEvent
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
super.onInterceptTouchEvent(ev);
return bTouchIntercept;
}
// view.onTouchEvent
@Override
public boolean onTouchEvent(MotionEvent event) {
bLockScrollX = false;
return super.onTouchEvent(event);
}
/**
* @author cheng.yang
*
* 自定義手勢(shì)監(jiān)聽
*/
public class LayoutGestureListener extends SimpleOnGestureListener {
@Override
public boolean onDown(MotionEvent e) {
bLockScrollX = true;
return super.onDown(e);
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
if(!bLockScrollX){
if(mOnScrollListenerCallback != null){
mOnScrollListenerCallback.doOnScroll(e1, e2, distanceX, distanceY);
}
}

if(Math.abs(e1.getY() - e2.getY()) > Math.abs(e1.getX() - e2.getX())){
return false;
}else{
return true;
}
}
}

public interface OnScrollListener {
void doOnScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY);
void doOnRelease();
}
}

這個(gè)控件中,含有一個(gè)接口,當(dāng)用戶手勢(shì)為左右時(shí),則需要將滾動(dòng)數(shù)據(jù)回傳到主視圖中去處理,而自己攔截不往下傳遞消息。里面有個(gè)消息流程圖,講的比較詳細(xì)了,大家可以先看看,有什么問(wèn)題,可以問(wèn)我。
三、MainActivity的實(shí)現(xiàn):
這個(gè)需要著講解,畢竟,左、右滑動(dòng)的實(shí)現(xiàn)都在這里。
復(fù)制代碼 代碼如下:

/**
*
* @author cheng.yang
*
* 左、右菜單滑出
*
* params[0]: 滑動(dòng)距離
* params[1]: 滑動(dòng)速度,帶方向
*/
public class SlideMenu extends AsyncTask<Integer, Integer, Void>{
@Override
protected Void doInBackground(Integer... params) {
if(params.length != 2){
Log.e(TAG, "error, params must have 2!");
}
int times = params[0] / Math.abs(params[1]);
if(params[0] % Math.abs(params[1]) != 0){
times ++;
}

for(int i = 0; i < times; i++){
this.publishProgress(params[0], params[1], i+1);
}

return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
if(values.length != 3){
Log.e(TAG, "error, values must have 3!");
}
int distance = Math.abs(values[1]) * values[2];
int delta = values[0] - distance;
int leftMargin = 0;
if(values[1] < 0){ // 左移
leftMargin = (delta > 0 ? values[1] : -(Math.abs(values[1]) - Math.abs(delta)));
}else{
leftMargin = (delta > 0 ? values[1] : (Math.abs(values[1]) - Math.abs(delta)));
}

rollLayout(leftMargin);
}

首先,自定義一個(gè)繼承于AsyncTask的類的線程,這個(gè)自定義類,就是用來(lái)實(shí)現(xiàn)動(dòng)畫效果,重在“滑動(dòng)”,而不是硬生生的擠出來(lái)。關(guān)于AsyncTask的用法,大家可以看我的博客中關(guān)于它的講解:
http://blog.csdn.net/qingye_love/article/details/8777508
自定義類的使用需要兩個(gè)參數(shù),一個(gè)是滑動(dòng)的距離,一個(gè)是每次滑動(dòng)多少且向哪個(gè)方向滑動(dòng):
1. 滑動(dòng)距離:若是左側(cè)菜單滑出來(lái),距離就是左側(cè)菜單的寬度;同理,右側(cè)滑出就是右側(cè)菜單的寬度;
2. 滑動(dòng)速度:即動(dòng)畫滑動(dòng)時(shí),向哪個(gè)方向,且每次滑動(dòng)多少像素;
在doInBackground中,計(jì)算需要滑動(dòng)多少次,然后用for循環(huán)調(diào)用publishProgress,實(shí)際上就是調(diào)用的onProgressUpdate,在onProgressUpdate中,根據(jù)方向,以及當(dāng)前main layout的 leftMargin來(lái)計(jì)算是滑動(dòng)指定的距離(速度),還是當(dāng)終點(diǎn)距離小于滑動(dòng)速度時(shí),速度就為終點(diǎn)距離,最終,調(diào)用rollLayout,來(lái)修改 leftLayout, mainLayout, rightLayout三者的布局,達(dá)到滑動(dòng)的效果。
rollLayout的實(shí)現(xiàn):
復(fù)制代碼 代碼如下:

private void rollLayout(int margin){
RelativeLayout.LayoutParams lp = (LayoutParams) mainLayout.getLayoutParams();
lp.leftMargin += margin;
lp.rightMargin -= margin;
mainLayout.setLayoutParams(lp);
lp = (LayoutParams) leftLayout.getLayoutParams();
lp.leftMargin += margin;
leftLayout.setLayoutParams(lp);
lp = (LayoutParams) rightLayout.getLayoutParams();
lp.leftMargin += margin;
lp.rightMargin -= margin;
rightLayout.setLayoutParams(lp);
}

這個(gè)就是修改三個(gè)layout的leftMargin和rightMargin。
初始化布局控件:
復(fù)制代碼 代碼如下:

private void initView(){
mainLayout = (RelativeLayout) findViewById(R.id.mainLayout);
leftLayout = (RelativeLayout) findViewById(R.id.leftLayout);
rightLayout = (RelativeLayout) findViewById(R.id.rightLayout);
mainLayout.setOnTouchListener(this);
leftLayout.setOnTouchListener(this);
rightLayout.setOnTouchListener(this);

layoutSlideMenu = (LayoutRelative) findViewById(R.id.layoutSlideMenu);
layoutSlideMenu.setOnScrollListener(new OnScrollListener(){
@Override
public void doOnScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
onScroll(distanceX);
}

@Override
public void doOnRelease(){
onRelease();
}
});

ivMore = (ImageView) findViewById(R.id.ivMore);
ivSettings = (ImageView) findViewById(R.id.ivSettings);
ivMore.setOnTouchListener(this);
ivSettings.setOnTouchListener(this);

mListMore = (ListView) findViewById(R.id.listMore);
mListMore.setAdapter(new ArrayAdapter<String>(this, R.layout.item, R.id.tv_item, title));
mListMore.setOnItemClickListener(this);

mGestureDetector = new GestureDetector(this);
mGestureDetector.setIsLongpressEnabled(false);

resizeLayout();
}

調(diào)整三個(gè)layout,將leftLayout移動(dòng)到屏幕最左邊之外,現(xiàn)時(shí)將rightLayout移動(dòng)到屏幕最右邊之外:
復(fù)制代碼 代碼如下:

/*
* 使用leftMargin及rightMargin防止layout被擠壓變形
* Math.abs(leftMargin - rightMargin) = layout.width
*/
private void resizeLayout(){
DisplayMetrics dm = getResources().getDisplayMetrics();

// 固定 main layout, 防止被左、右擠壓變形
RelativeLayout.LayoutParams lp = (LayoutParams) mainLayout.getLayoutParams();
lp.width = dm.widthPixels;
mainLayout.setLayoutParams(lp);

// 將左layout調(diào)整至main layout左邊
lp = (LayoutParams) leftLayout.getLayoutParams();
lp.leftMargin = -lp.width;
leftLayout.setLayoutParams(lp);
Log.d(TAG, "left l.margin = " + lp.leftMargin);

// 將左layout調(diào)整至main layout右邊
lp = (LayoutParams) rightLayout.getLayoutParams();
lp.leftMargin = dm.widthPixels;
lp.rightMargin = -lp.width;
rightLayout.setLayoutParams(lp);
Log.d(TAG, "right l.margin = " + lp.leftMargin);
}

重載onTouch,處理消息:
復(fù)制代碼 代碼如下:

////////////////////////////// onTouch ///////////////////////////////
@Override
public boolean onTouch(View v, MotionEvent event) {
mClickedView = v;

if(MotionEvent.ACTION_UP == event.getAction() && bIsScrolling){
onRelease();
}

return mGestureDetector.onTouchEvent(event);
}

記錄選擇的view,并將消息傳給GestureDetector;
重載GestureDetector的onDown, onScroll, onSingleTapUp這三個(gè)是主要的,其它的重載不做任何修改;
復(fù)制代碼 代碼如下:

/////////////////// GestureDetector Override Begin ///////////////////
@Override
public boolean onDown(MotionEvent e) {

bIsScrolling = false;
mScroll = 0;
iLimited = 0;
RelativeLayout.LayoutParams lp = (LayoutParams) mainLayout.getLayoutParams();
if(lp.leftMargin > 0){
iLimited = 1;
}else if(lp.leftMargin < 0){
iLimited = -1;
}

return true;
}

在onDown中,判斷當(dāng)前的 mainLayout 的 leftMargin 的值,并用 iLimited 記錄下來(lái),原因:
如果當(dāng)前顯示的是左側(cè)菜單,則用戶滾動(dòng)屏幕時(shí),最多只是將左側(cè)菜單滑出到屏幕外,而不會(huì)繼續(xù)滑動(dòng),將右側(cè)菜單顯示出來(lái);同理,當(dāng)前已經(jīng)顯示了右側(cè)菜單。
復(fù)制代碼 代碼如下:

@Override
public boolean onSingleTapUp(MotionEvent e) {
/*
* 正常情況下,mainLayout的leftMargin為0,
* 當(dāng)左/右菜單為打開中,此時(shí)就不為0,需要判斷
*/
if(mClickedView != null){
RelativeLayout.LayoutParams lp = (LayoutParams) mainLayout.getLayoutParams();

if(mClickedView == ivMore){
Log.d(TAG, "[onSingleTapUp] ivMore clicked! leftMargin = " + lp.leftMargin);

if(lp.leftMargin == 0){
new SlideMenu().execute(leftLayout.getLayoutParams().width, SPEED);
}else{
new SlideMenu().execute(leftLayout.getLayoutParams().width, -SPEED);
}
}else if(mClickedView == ivSettings){
Log.d(TAG, "[onSingleTapUp] ivSettings clicked! leftMargin = " + lp.leftMargin);

if(lp.leftMargin == 0){
new SlideMenu().execute(rightLayout.getLayoutParams().width, -SPEED);
}else{
new SlideMenu().execute(rightLayout.getLayoutParams().width, SPEED);
}
}else if(mClickedView == mainLayout){
Log.d(TAG, "[onSingleTapUp] mainLayout clicked!");
}
}
return true;
}

這個(gè)函數(shù)中,處理標(biāo)題欄左、右兩個(gè)按鈕,點(diǎn)擊一次,顯示側(cè)導(dǎo)航菜單,再點(diǎn)擊一次,則關(guān)閉。
復(fù)制代碼 代碼如下:

@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
onScroll(distanceX);
return false;
}

滾動(dòng)處理,直接將本次的水平滾動(dòng)距離傳給onScroll函數(shù)來(lái)處理,因?yàn)椋瑐?cè)邊導(dǎo)航菜單的水平滾動(dòng)也將通過(guò)OnScrollListener.doOnScroll來(lái)回調(diào),所以,寫個(gè)通用函數(shù)。
復(fù)制代碼 代碼如下:

private void onScroll(float distanceX){
bIsScrolling = true;
mScroll += distanceX; // 向左為正
Log.d(TAG, "mScroll = " + mScroll + ", distanceX = " + distanceX);

RelativeLayout.LayoutParams lp = (LayoutParams) mainLayout.getLayoutParams();
RelativeLayout.LayoutParams lpLeft = (LayoutParams) leftLayout.getLayoutParams();
RelativeLayout.LayoutParams lpRight = (LayoutParams) rightLayout.getLayoutParams();
Log.d(TAG, "lp.leftMargin = " + lp.leftMargin);

int distance = 0;
if(mScroll > 0){ // 向左移動(dòng)
if(lp.leftMargin <= 0){ // 打開右導(dǎo)航菜單
if(iLimited > 0){
return;
}
distance = lpRight.width - Math.abs(lp.leftMargin);
}else if(lp.leftMargin > 0){ // 關(guān)閉左導(dǎo)航菜單
distance = lp.leftMargin;
}
if(mScroll >= distance){
mScroll = distance;
}
}else if(mScroll < 0){ // 向右移動(dòng)
if(lp.leftMargin >= 0){ // 打開左導(dǎo)航菜單
if(iLimited < 0){
return;
}
distance = lpLeft.width - Math.abs(lp.leftMargin);
}else if(lp.leftMargin < 0){ // 關(guān)閉右導(dǎo)航菜單
distance = Math.abs(lp.leftMargin);
}
if(mScroll <= -distance){
mScroll = -distance;
}
}
Log.d(TAG, "mScroll = " + mScroll);
if(mScroll != 0){
rollLayout(-mScroll);
}
}

接下來(lái),我們看看當(dāng)側(cè)邊導(dǎo)航菜單,水平滾動(dòng)且用戶松開手指時(shí),回調(diào)OnScrollListener.doOnRelease,我們會(huì)調(diào)用一個(gè)onRelease來(lái)負(fù)責(zé)處理收尾工作:
復(fù)制代碼 代碼如下:

private void onRelease(){
RelativeLayout.LayoutParams lp = (LayoutParams) mainLayout.getLayoutParams();
if(lp.leftMargin < 0){ // 左移
/*
* 左移大于右導(dǎo)航寬度一半,則自動(dòng)展開,否則自動(dòng)縮回去
*/
if(Math.abs(lp.leftMargin) > rightLayout.getLayoutParams().width/2){
new SlideMenu().execute(rightLayout.getLayoutParams().width - Math.abs(lp.leftMargin), -SPEED);
}else{
new SlideMenu().execute(Math.abs(lp.leftMargin), SPEED);
}
}else if(lp.leftMargin > 0){
/*
* 右移大于左導(dǎo)航寬度一半,則自動(dòng)展開,否則自動(dòng)縮回去
*/
if(Math.abs(lp.leftMargin) > leftLayout.getLayoutParams().width/2){
new SlideMenu().execute(leftLayout.getLayoutParams().width - Math.abs(lp.leftMargin), SPEED);
}else{
new SlideMenu().execute(Math.abs(lp.leftMargin), -SPEED);
}
}
}

主要的代碼塊已經(jīng)講解完了,下面是完整的代碼:
復(fù)制代碼 代碼如下:

package com.chris.lr.slidemenu;
import com.chris.lr.slidemenu.LayoutRelative.OnScrollListener;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.Toast;
import android.widget.RelativeLayout.LayoutParams;
import android.app.Activity;
public class MainActivity extends Activity implements OnGestureListener,
OnTouchListener, OnItemClickListener {
private static final String TAG = "ChrisSlideMenu";
private RelativeLayout mainLayout;
private RelativeLayout leftLayout;
private RelativeLayout rightLayout;
private LayoutRelative layoutSlideMenu;
private ListView mListMore;

private ImageView ivMore;
private ImageView ivSettings;
private GestureDetector mGestureDetector;

private static final int SPEED = 30;
private boolean bIsScrolling = false;
private int iLimited = 0;
private int mScroll = 0;
private View mClickedView = null;

private String title[] = {"待發(fā)送隊(duì)列",
"同步分享設(shè)置",
"編輯我的資料",
"找朋友",
"告訴朋友",
"節(jié)省流量",
"推送設(shè)置",
"版本更新",
"意見反饋",
"積分兌換",
"精品應(yīng)用",
"常見問(wèn)題",
"退出當(dāng)前帳號(hào)",
"退出1",
"退出2",
"退出3",
"退出4"};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
initView();
}

private void initView(){
mainLayout = (RelativeLayout) findViewById(R.id.mainLayout);
leftLayout = (RelativeLayout) findViewById(R.id.leftLayout);
rightLayout = (RelativeLayout) findViewById(R.id.rightLayout);
mainLayout.setOnTouchListener(this);
leftLayout.setOnTouchListener(this);
rightLayout.setOnTouchListener(this);

layoutSlideMenu = (LayoutRelative) findViewById(R.id.layoutSlideMenu);
layoutSlideMenu.setOnScrollListener(new OnScrollListener(){
@Override
public void doOnScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
onScroll(distanceX);
}

@Override
public void doOnRelease(){
onRelease();
}
});

ivMore = (ImageView) findViewById(R.id.ivMore);
ivSettings = (ImageView) findViewById(R.id.ivSettings);
ivMore.setOnTouchListener(this);
ivSettings.setOnTouchListener(this);

mListMore = (ListView) findViewById(R.id.listMore);
mListMore.setAdapter(new ArrayAdapter<String>(
this, R.layout.item, R.id.tv_item, title));
mListMore.setOnItemClickListener(this);

mGestureDetector = new GestureDetector(this);
mGestureDetector.setIsLongpressEnabled(false);

resizeLayout();
}

/*
* 使用leftMargin及rightMargin防止layout被擠壓變形
* Math.abs(leftMargin - rightMargin) = layout.width
*/
private void resizeLayout(){
DisplayMetrics dm = getResources().getDisplayMetrics();

// 固定 main layout, 防止被左、右擠壓變形
RelativeLayout.LayoutParams lp =
(LayoutParams) mainLayout.getLayoutParams();
lp.width = dm.widthPixels;
mainLayout.setLayoutParams(lp);

// 將左layout調(diào)整至main layout左邊
lp = (LayoutParams) leftLayout.getLayoutParams();
lp.leftMargin = -lp.width;
leftLayout.setLayoutParams(lp);
Log.d(TAG, "left l.margin = " + lp.leftMargin);

// 將左layout調(diào)整至main layout右邊
lp = (LayoutParams) rightLayout.getLayoutParams();
lp.leftMargin = dm.widthPixels;
lp.rightMargin = -lp.width;
rightLayout.setLayoutParams(lp);
Log.d(TAG, "right l.margin = " + lp.leftMargin);
}

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0){
RelativeLayout.LayoutParams lp =
(LayoutParams) mainLayout.getLayoutParams();

if(lp.leftMargin != 0){
if(lp.leftMargin > 0){
new SlideMenu().execute(
leftLayout.getLayoutParams().width, -SPEED);
}else if(lp.leftMargin < 0){
new SlideMenu().execute(
rightLayout.getLayoutParams().width, SPEED);
}
return true;
}
}
return super.onKeyDown(keyCode, event);
}

private void rollLayout(int margin){
RelativeLayout.LayoutParams lp =
(LayoutParams) mainLayout.getLayoutParams();
lp.leftMargin += margin;
lp.rightMargin -= margin;
mainLayout.setLayoutParams(lp);
lp = (LayoutParams) leftLayout.getLayoutParams();
lp.leftMargin += margin;
leftLayout.setLayoutParams(lp);
lp = (LayoutParams) rightLayout.getLayoutParams();
lp.leftMargin += margin;
lp.rightMargin -= margin;
rightLayout.setLayoutParams(lp);
}
private void onScroll(float distanceX){
bIsScrolling = true;
mScroll += distanceX; // 向左為正
Log.d(TAG, "mScroll = " + mScroll + ", distanceX = " + distanceX);

RelativeLayout.LayoutParams lp =
(LayoutParams) mainLayout.getLayoutParams();
RelativeLayout.LayoutParams lpLeft =
(LayoutParams) leftLayout.getLayoutParams();
RelativeLayout.LayoutParams lpRight =
(LayoutParams) rightLayout.getLayoutParams();
Log.d(TAG, "lp.leftMargin = " + lp.leftMargin);

int distance = 0;
if(mScroll > 0){ // 向左移動(dòng)
if(lp.leftMargin <= 0){ // 打開右導(dǎo)航菜單
if(iLimited > 0){
return;
}
distance = lpRight.width - Math.abs(lp.leftMargin);
}else if(lp.leftMargin > 0){ // 關(guān)閉左導(dǎo)航菜單
distance = lp.leftMargin;
}
if(mScroll >= distance){
mScroll = distance;
}
}else if(mScroll < 0){ // 向右移動(dòng)
if(lp.leftMargin >= 0){ // 打開左導(dǎo)航菜單
if(iLimited < 0){
return;
}
distance = lpLeft.width - Math.abs(lp.leftMargin);
}else if(lp.leftMargin < 0){ // 關(guān)閉右導(dǎo)航菜單
distance = Math.abs(lp.leftMargin);
}
if(mScroll <= -distance){
mScroll = -distance;
}
}
Log.d(TAG, "mScroll = " + mScroll);
if(mScroll != 0){
rollLayout(-mScroll);
}
}

private void onRelease(){
RelativeLayout.LayoutParams lp =
(LayoutParams) mainLayout.getLayoutParams();
if(lp.leftMargin < 0){ // 左移
/*
* 左移大于右導(dǎo)航寬度一半,則自動(dòng)展開,否則自動(dòng)縮回去
*/
if(Math.abs(lp.leftMargin) > rightLayout.getLayoutParams().width/2){
new SlideMenu().execute(rightLayout.getLayoutParams().width -
Math.abs(lp.leftMargin), -SPEED);
}else{
new SlideMenu().execute(Math.abs(lp.leftMargin), SPEED);
}
}else if(lp.leftMargin > 0){
/*
* 右移大于左導(dǎo)航寬度一半,則自動(dòng)展開,否則自動(dòng)縮回去
*/
if(Math.abs(lp.leftMargin) > leftLayout.getLayoutParams().width/2){
new SlideMenu().execute(leftLayout.getLayoutParams().width -
Math.abs(lp.leftMargin), SPEED);
}else{
new SlideMenu().execute(Math.abs(lp.leftMargin), -SPEED);
}
}
}
///////////////////// ListView.onItemClick ///////////////////////
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
Toast.makeText(this, title[arg2], Toast.LENGTH_SHORT).show();
}

////////////////////////////// onTouch ///////////////////////////////
@Override
public boolean onTouch(View v, MotionEvent event) {
mClickedView = v;

if(MotionEvent.ACTION_UP == event.getAction() && bIsScrolling){
onRelease();
}

return mGestureDetector.onTouchEvent(event);
}

/////////////////// GestureDetector Override Begin ///////////////////
@Override
public boolean onDown(MotionEvent e) {

bIsScrolling = false;
mScroll = 0;
iLimited = 0;
RelativeLayout.LayoutParams lp =
(LayoutParams) mainLayout.getLayoutParams();
if(lp.leftMargin > 0){
iLimited = 1;
}else if(lp.leftMargin < 0){
iLimited = -1;
}

return true;
}
@Override
public boolean onFling(MotionEvent arg0, MotionEvent arg1, float arg2,
float arg3) {
return false;
}
@Override
public void onLongPress(MotionEvent e) {

}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
onScroll(distanceX);
return false;
}
@Override
public void onShowPress(MotionEvent arg0) {

}
@Override
public boolean onSingleTapUp(MotionEvent e) {
/*
* 正常情況下,mainLayout的leftMargin為0,
* 當(dāng)左/右菜單為打開中,此時(shí)就不為0,需要判斷
*/
if(mClickedView != null){
RelativeLayout.LayoutParams lp =
(LayoutParams) mainLayout.getLayoutParams();

if(mClickedView == ivMore){
Log.d(TAG, "[onSingleTapUp] ivMore clicked! leftMargin = "
+ lp.leftMargin);

if(lp.leftMargin == 0){
new SlideMenu().execute(
leftLayout.getLayoutParams().width, SPEED);
}else{
new SlideMenu().execute(
leftLayout.getLayoutParams().width, -SPEED);
}
}else if(mClickedView == ivSettings){
Log.d(TAG, "[onSingleTapUp] ivSettings clicked! leftMargin = "
+ lp.leftMargin);

if(lp.leftMargin == 0){
new SlideMenu().execute(
rightLayout.getLayoutParams().width, -SPEED);
}else{
new SlideMenu().execute(
rightLayout.getLayoutParams().width, SPEED);
}
}else if(mClickedView == mainLayout){
Log.d(TAG, "[onSingleTapUp] mainLayout clicked!");
}
}
return true;
}
/////////////////// GestureDetector Override End ///////////////////

/**
*
* @author cheng.yang
*
* 左、右菜單滑出
*
* params[0]: 滑動(dòng)距離
* params[1]: 滑動(dòng)速度,帶方向
*/
public class SlideMenu extends AsyncTask<Integer, Integer, Void>{
@Override
protected Void doInBackground(Integer... params) {
if(params.length != 2){
Log.e(TAG, "error, params must have 2!");
}
int times = params[0] / Math.abs(params[1]);
if(params[0] % Math.abs(params[1]) != 0){
times ++;
}

for(int i = 0; i < times; i++){
this.publishProgress(params[0], params[1], i+1);
}

return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
if(values.length != 3){
Log.e(TAG, "error, values must have 3!");
}
int distance = Math.abs(values[1]) * values[2];
int delta = values[0] - distance;
int leftMargin = 0;
if(values[1] < 0){ // 左移
leftMargin = (delta > 0 ? values[1] :
-(Math.abs(values[1]) - Math.abs(delta)));
}else{
leftMargin = (delta > 0 ? values[1] :
(Math.abs(values[1]) - Math.abs(delta)));
}

rollLayout(leftMargin);
}
}
}

總結(jié):
本文左,右滑出菜單的原理,就是用到了leftMargin和rightMargin兩個(gè)屬性,并配合幾個(gè)GestureDetector來(lái)完全手勢(shì)判斷的邏輯處理,其中,自定義的那個(gè)控件布局LayoutRelative,可以用在任何子視圖中需要處理上下,左右滾動(dòng)沖突的地方,良好的解決了沖突問(wèn)題。
完整的代碼下載地址:
http://download.csdn.net/detail/qingye_love/5237799
希望大家多來(lái)聊聊,同時(shí),大家可以在我的基礎(chǔ)上,實(shí)現(xiàn)onFling的方法,歡迎交流。
發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 视频h在线| 国产精品成人亚洲一区二区 | 精品在线观看一区二区 | 国产精品免费久久久久 | 国产精品嘿咻嘿咻在线播放 | 亚洲国产精品二区 | 亚洲精品久久久久www | 夜添久久精品亚洲国产精品 | 91色爱 | 92看片淫黄大片欧美看国产片 | 午夜影视一区二区 | 欧美人禽| 国产一区二区三区视频观看 | 国产成人高潮免费观看精品 | 欧美一级黄视频 | 亚洲男人的天堂在线视频 | 日日操夜| 国产一级做a | 国产成人精品午夜视频' | 一级做受毛片免费大片 | 国产麻豆久久 | 羞羞答答影院 | 久久国产免费 | 国产精品久久久久久久久久尿 | 日本中文字幕久久 | 免费毛片播放 | 狠狠操视频网站 | 欧美精品电影一区 | 少妇一级淫片免费看 | 欧美性生活区 | 亚洲成人福利网站 | 日韩毛片毛片久久精品 | 中文在线日韩 | 日本欧美视频 | 黄色高清av| 久久国产秒 | 精品一区二区三区网站 | 水多视频在线观看 | 毛片免费大全短视频 | 亚洲精品在线观看网站 | 免看一级片 |