Android實(shí)現(xiàn)自定義左右菜單
功能描述: 在左中右三個(gè)區(qū)域分別承載三個(gè)不同的view,把它全部添加進(jìn)來,而我們實(shí)現(xiàn)左右菜單,就是來控制當(dāng)前顯示的是哪一部分;如果顯示中間菜單,就把中間菜單呈現(xiàn)在用戶面前;如果顯示左菜單,就把左菜單和一部分的中間菜單呈現(xiàn)在用戶面前;如果顯示右菜單,就把右菜單和一部分的中間菜單呈現(xiàn)在用戶面前。即整張圖的左中右不斷移動(dòng)。
功能實(shí)現(xiàn) (step by step): 1、菜單布局(通過擴(kuò)展,完成基本布局。包含左右菜單布局和中間內(nèi)容區(qū)域,并設(shè)置顏色用于區(qū)分)2、菜單左右滑動(dòng)(使用邏輯,找出滑動(dòng)中間點(diǎn),并對(duì)事件作處理,主要針對(duì)滑動(dòng)事件)3、加入左右滑動(dòng)動(dòng)畫(使用Scroll進(jìn)行滑動(dòng)事件處理,主要是自動(dòng)滑動(dòng),增加用戶體驗(yàn))4、處理點(diǎn)擊事件(通過對(duì)左右事件的處理,會(huì)阻止點(diǎn)擊事件的處理,這里需要調(diào)用系統(tǒng),返還點(diǎn)擊事件)5、添加蒙板效果(通過增加view,對(duì)view的透明度進(jìn)行處理,從而實(shí)現(xiàn)蒙版效果)
核心內(nèi)容: 1.布局的添加 2.事件分發(fā)機(jī)制 3.滾動(dòng)添加 4.蒙版添加
構(gòu)想圖:
開發(fā)過程中遇到的錯(cuò)誤: Caused by:java.lang.IllegalStateException:The specified child already has a parent.You must call removeView() on the child’s parent first. 造成這個(gè)原因,是組件在父類中重復(fù)加載了相同的組件 如: addView(leftMenu, mLayout); // 第一次添加 addView((leftMenu, , mLayout); // 第二次添加
源代碼:
activity_main.xml:
<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>
left.xml:
<?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 " > <Button
android:id ="@+id/button1" android:layout_width ="wrap_content" android:layout_height ="wrap_content" android:text ="Button" /> </LinearLayout >
MainActivity.java
package com.example.mymenu;
import android.os.Bundle;
import android.app.Activity;
import android.support.v4.app.FragmentActivity;
import android.view.Menu;
public class MainActivity extends FragmentActivity {private MainUI mainUI ;
private LeftMenu leftMenu ;
@Override protected void onCreate (Bundle savedInstanceState) {
super .onCreate(savedInstanceState);mainUI=
new MainUI(
this );setContentView( mainUI);leftMenu=
new LeftMenu();getSupportFragmentManager().beginTransaction().add(MainUI. LEFT_ID,leftMenu ).commit();}}
MainUI.java
package com.example.mymenu;
import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Color; import android.graphics.Point; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.animation.DecelerateInterpolator; import android.widget.FrameLayout; import android.widget.RelativeLayout; import android.widget.Scroller;
@SuppressLint(“NewApi” ) public class MainUI extends RelativeLayout{
private Context context ;private FrameLayout leftMenu ;private FrameLayout middleMenu ;private FrameLayout rightMenu ;//設(shè)置蒙版private FrameLayout middleMask ;private Scroller mScroller ;//定義左中右菜單的IDpublic static final int LEFT_ID=0xaabbcc;public static final int MIDDLE_ID=0xaaccbb;public static final int RIGHT_ID=0xccbbaa;//繼承自RelativeLayout,并實(shí)現(xiàn)兩個(gè)構(gòu)造方法public MainUI(Context context) {super(context);initView(context);}public MainUI(Context context, AttributeSet attrs) {super(context, attrs);initView(context);}public void initView(Context context){this.context =context;mScroller=new Scroller(context, new DecelerateInterpolator()); //渲染器//實(shí)例化三個(gè)區(qū)域leftMenu= new FrameLayout(context);middleMenu=new FrameLayout(context);rightMenu=new FrameLayout(context);middleMask=new FrameLayout(context);//分別為三個(gè)區(qū)域添加不同的顏色leftMenu.setBackgroundColor(Color. RED);middleMenu.setBackgroundColor(Color.GREEN);rightMenu.setBackgroundColor(Color.RED);middleMask.setBackgroundColor(0x88000000);//蒙版的初始顏色為淺灰色//為自定義的左中右菜單添加IDleftMenu.setId( LEFT_ID);middleMenu.setId(MIDDLE_ID );rightMenu.setId(RIGHT_ID ); //把三個(gè)區(qū)域全部填充到一個(gè)view中,實(shí)際上這個(gè)view就是我們承載三個(gè)區(qū)域的最外層的RelativeLayout,也是當(dāng)前的主文件addView( leftMenu);addView( middleMenu);addView( rightMenu);addView( middleMask);middleMask.setAlpha(0);//設(shè)置蒙版的科技度(完全可見),只有在滑動(dòng)的時(shí)候才是不可見的}public float onMiddleMask(){System. out.println("透明度:" +middleMask .getAlpha());return middleMask .getAlpha(); }//根據(jù)滑動(dòng)的距離的變化來設(shè)置可見度值的變化@Overridepublic void scrollTo(int x, int y) {super.scrollTo(x, y);onMiddleMask();int curX=Math.abs(getScrollX());float scale=curX/(float)leftMenu.getMeasuredWidth(); //計(jì)算整個(gè)可見寬度middleMask.setAlpha(scale);}//在把三個(gè)區(qū)域添加到Layout之前,先對(duì)它們的寬和高進(jìn)行測(cè)量@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//widthMeasureSpec,heightMeasureSpec是當(dāng)前屏幕的寬度和高度super.onMeasure(widthMeasureSpec, heightMeasureSpec);//中間菜單的寬和高即為當(dāng)前屏幕的寬和高middleMenu.measure(widthMeasureSpec, heightMeasureSpec);//獲取蒙版的寬和高middleMask.measure(widthMeasureSpec, heightMeasureSpec);//得到當(dāng)前屏幕的真實(shí)寬度int realWidth=MeasureSpec.getSize(widthMeasureSpec);//計(jì)算所需要的寬度int tempWidthMeasure=MeasureSpec.makeMeasureSpec(( int)(realWidth*0.8f),MeasureSpec.EXACTLY);//為左右菜單設(shè)置相應(yīng)的寬度leftMenu.measure(tempWidthMeasure, heightMeasureSpec);rightMenu.measure(tempWidthMeasure, heightMeasureSpec); }//把三個(gè)區(qū)域添加到Layout中@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {//五個(gè)參數(shù)(改變監(jiān)聽,left,top,right,bottom)super.onLayout(changed, l, t, r, b);//設(shè)置三個(gè)區(qū)域在layout中的位置middleMenu.layout(l, t, r, b);middleMask.layout(l, t, r, b);leftMenu.layout(l- leftMenu.getMeasuredWidth(), t, l, b);//(l-leftMenu.getMeasuredWidth(), t, r, b)rightMenu.layout(r,t,r+ rightMenu.getMeasuredWidth(),b); //(l+middleMenu.getMeasuredWidth(), t, l+middleMenu.getMeasuredWidth()+rightMenu.getMeasuredWidth(), b)}private boolean isTestCompete ;private boolean isleftrightEvent ;//標(biāo)識(shí)上下滑動(dòng)和左右滑動(dòng),左右滑動(dòng)為true,上下滑動(dòng)為false//事件分發(fā)@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {//檢測(cè)到一個(gè)事件,判斷是什么事件(上下滑動(dòng)事件、左右滑動(dòng)事件、點(diǎn)擊事件)if (!isTestCompete ) {//沒有初始化,默認(rèn)為falsegetEventType(ev); //創(chuàng)建一個(gè)判斷處理事件的方法return true ;}if (isleftrightEvent ) {//左右滑動(dòng)應(yīng)該觸發(fā)的響應(yīng)switch (ev.getActionMasked()) {case MotionEvent.ACTION_MOVE://你手指滑動(dòng)的距離即為當(dāng)前屏幕滑動(dòng)的距離int curScrollX=getScrollX();//定義屏幕滑動(dòng)的距離int dis_x=(int )(ev.getX()-point.x);//定義手指滑動(dòng)的距離int expectX=-dis_x+curScrollX;//根據(jù)差值判斷是向左滑動(dòng)還是向右滑動(dòng)(差值大于20是向右滑動(dòng);差值小于20是向左滑動(dòng))int finalX=0; //定義屏幕最終的左右滑動(dòng)距離if (expectX<0) {//向左finalX=Math. max(expectX, -leftMenu.getMeasuredWidth());//最大值} else{ //向右finalX=Math. min(expectX, rightMenu.getMeasuredWidth());//最小值}scrollTo(finalX, 0); //使屏幕移動(dòng)到最終的位置point. x=( int)ev.getX();break;case MotionEvent.ACTION_UP:case MotionEvent.ACTION_CANCEL:curScrollX=getScrollX();if (Math.abs(curScrollX)> leftMenu.getMeasuredWidth() >> 1) {//當(dāng)屏幕滑動(dòng)的距離大于左右菜單的一半時(shí),自動(dòng)跳到相應(yīng)的菜單if (curScrollX<0) {mScroller.startScroll(curScrollX,0,- leftMenu.getMeasuredWidth()-curScrollX, 0,200); //起始X,起始Y,終止X,終止Y} else {mScroller.startScroll(curScrollX,0,leftMenu.getMeasuredWidth()-curScrollX, 0,200);}} else {//返回原點(diǎn)mScroller.startScroll(curScrollX,0,-curScrollX, 0,200);}invalidate(); //調(diào)用view的重繪方法進(jìn)行刷新isleftrightEvent=false ;isTestCompete=false ; break;}} else{//對(duì)上下滑動(dòng)和點(diǎn)擊觸發(fā)的響應(yīng)switch (ev.getActionMasked()) {case MotionEvent.ACTION_UP:isleftrightEvent=false ;isTestCompete=false ;break;default: break;}}return super .dispatchTouchEvent(ev);}//重寫回調(diào)方法,使左右菜單進(jìn)行滑動(dòng)@Overridepublic void computeScroll() {super.computeScroll();if (mScroller .computeScrollOffset()) {return;}int tempX=mScroller .getCurrX();scrollTo(tempX, 0);}private Point point =new Point(); //獲取當(dāng)前屏幕的點(diǎn),通過點(diǎn)來計(jì)算滑動(dòng)的距離,根據(jù)滑動(dòng)的距離來判斷相應(yīng)的事件是滑動(dòng)還是點(diǎn)擊private static final int TEST_DIS=20; //如果移動(dòng)距離大于20就定義為滑動(dòng)//判斷處理事件private void getEventType(MotionEvent ev) {switch (ev.getActionMasked()) {case MotionEvent.ACTION_DOWN:point. x=( int) ev.getX();//floatpoint. y=( int) ev.getY();super.dispatchTouchEvent(ev);break;case MotionEvent.ACTION_MOVE:int dX= Math.abs(( int)ev.getX()-point .x );//獲取水平方向上移動(dòng)距離的絕對(duì)值int dY=Math.abs(( int)ev.getY()-point .y );//獲取垂直方向上移動(dòng)距離的絕對(duì)值if (dX>=TEST_DIS && dX>dY) {//左右滑動(dòng)isleftrightEvent=true ;isTestCompete=true ; //允許進(jìn)入接下來的返出測(cè)試階段//獲取當(dāng)前點(diǎn)的 xy坐標(biāo)point. x=( int) ev.getX();//floatpoint. y=( int) ev.getY();} else if (dY>=TEST_DIS && dY>dX) { //上下滑動(dòng)isleftrightEvent=false ;isTestCompete=true ;//允許進(jìn)入接下來的返出測(cè)試階段point. x=( int) ev.getX();//floatpoint. y=( int) ev.getY();}break;case MotionEvent.ACTION_UP:case MotionEvent.ACTION_CANCEL://觸摸到屏幕的邊緣//對(duì)點(diǎn)擊事件進(jìn)行處理(返還給系統(tǒng)處理)super.dispatchTouchEvent(ev);isleftrightEvent=false ;isTestCompete=false ;break;}}
}
LeftMeau.java:
package com.example.mymenu;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
public class LeftMenu extends Fragment {@Override public View
onCreateView (LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {View v=inflater.inflate(R.layout. left, container,
false );v.findViewById(R.id. button1).setOnClickListener(
new OnClickListener() {
@Override public void onClick (View v) {System. out.println(
"Hello LMB!!" ); }});
return v;}
}
總結(jié)
以上是生活随笔 為你收集整理的Android项目开发实战—自定义左右菜单 的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔 推薦給好友。