Android實現自定義左右菜單
功能描述: 在左中右三個區域分別承載三個不同的view,把它全部添加進來,而我們實現左右菜單,就是來控制當前顯示的是哪一部分;如果顯示中間菜單,就把中間菜單呈現在用戶面前;如果顯示左菜單,就把左菜單和一部分的中間菜單呈現在用戶面前;如果顯示右菜單,就把右菜單和一部分的中間菜單呈現在用戶面前。即整張圖的左中右不斷移動。
功能實現 (step by step): 1、菜單布局(通過擴展,完成基本布局。包含左右菜單布局和中間內容區域,并設置顏色用于區分)2、菜單左右滑動(使用邏輯,找出滑動中間點,并對事件作處理,主要針對滑動事件)3、加入左右滑動動畫(使用Scroll進行滑動事件處理,主要是自動滑動,增加用戶體驗)4、處理點擊事件(通過對左右事件的處理,會阻止點擊事件的處理,這里需要調用系統,返還點擊事件)5、添加蒙板效果(通過增加view,對view的透明度進行處理,從而實現蒙版效果)
核心內容: 1.布局的添加 2.事件分發機制 3.滾動添加 4.蒙版添加
構想圖:
開發過程中遇到的錯誤: Caused by:java.lang.IllegalStateException:The specified child already has a parent.You must call removeView() on the child’s parent first. 造成這個原因,是組件在父類中重復加載了相同的組件 如: 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 ;//設置蒙版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,并實現兩個構造方法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()); //渲染器//實例化三個區域leftMenu= new FrameLayout(context);middleMenu=new FrameLayout(context);rightMenu=new FrameLayout(context);middleMask=new FrameLayout(context);//分別為三個區域添加不同的顏色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 ); //把三個區域全部填充到一個view中,實際上這個view就是我們承載三個區域的最外層的RelativeLayout,也是當前的主文件addView( leftMenu);addView( middleMenu);addView( rightMenu);addView( middleMask);middleMask.setAlpha(0);//設置蒙版的科技度(完全可見),只有在滑動的時候才是不可見的}public float onMiddleMask(){System. out.println("透明度:" +middleMask .getAlpha());return middleMask .getAlpha(); }//根據滑動的距離的變化來設置可見度值的變化@Overridepublic void scrollTo(int x, int y) {super.scrollTo(x, y);onMiddleMask();int curX=Math.abs(getScrollX());float scale=curX/(float)leftMenu.getMeasuredWidth(); //計算整個可見寬度middleMask.setAlpha(scale);}//在把三個區域添加到Layout之前,先對它們的寬和高進行測量@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//widthMeasureSpec,heightMeasureSpec是當前屏幕的寬度和高度super.onMeasure(widthMeasureSpec, heightMeasureSpec);//中間菜單的寬和高即為當前屏幕的寬和高middleMenu.measure(widthMeasureSpec, heightMeasureSpec);//獲取蒙版的寬和高middleMask.measure(widthMeasureSpec, heightMeasureSpec);//得到當前屏幕的真實寬度int realWidth=MeasureSpec.getSize(widthMeasureSpec);//計算所需要的寬度int tempWidthMeasure=MeasureSpec.makeMeasureSpec(( int)(realWidth*0.8f),MeasureSpec.EXACTLY);//為左右菜單設置相應的寬度leftMenu.measure(tempWidthMeasure, heightMeasureSpec);rightMenu.measure(tempWidthMeasure, heightMeasureSpec); }//把三個區域添加到Layout中@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {//五個參數(改變監聽,left,top,right,bottom)super.onLayout(changed, l, t, r, b);//設置三個區域在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 ;//標識上下滑動和左右滑動,左右滑動為true,上下滑動為false//事件分發@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {//檢測到一個事件,判斷是什么事件(上下滑動事件、左右滑動事件、點擊事件)if (!isTestCompete ) {//沒有初始化,默認為falsegetEventType(ev); //創建一個判斷處理事件的方法return true ;}if (isleftrightEvent ) {//左右滑動應該觸發的響應switch (ev.getActionMasked()) {case MotionEvent.ACTION_MOVE://你手指滑動的距離即為當前屏幕滑動的距離int curScrollX=getScrollX();//定義屏幕滑動的距離int dis_x=(int )(ev.getX()-point.x);//定義手指滑動的距離int expectX=-dis_x+curScrollX;//根據差值判斷是向左滑動還是向右滑動(差值大于20是向右滑動;差值小于20是向左滑動)int finalX=0; //定義屏幕最終的左右滑動距離if (expectX<0) {//向左finalX=Math. max(expectX, -leftMenu.getMeasuredWidth());//最大值} else{ //向右finalX=Math. min(expectX, rightMenu.getMeasuredWidth());//最小值}scrollTo(finalX, 0); //使屏幕移動到最終的位置point. x=( int)ev.getX();break;case MotionEvent.ACTION_UP:case MotionEvent.ACTION_CANCEL:curScrollX=getScrollX();if (Math.abs(curScrollX)> leftMenu.getMeasuredWidth() >> 1) {//當屏幕滑動的距離大于左右菜單的一半時,自動跳到相應的菜單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 {//返回原點mScroller.startScroll(curScrollX,0,-curScrollX, 0,200);}invalidate(); //調用view的重繪方法進行刷新isleftrightEvent=false ;isTestCompete=false ; break;}} else{//對上下滑動和點擊觸發的響應switch (ev.getActionMasked()) {case MotionEvent.ACTION_UP:isleftrightEvent=false ;isTestCompete=false ;break;default: break;}}return super .dispatchTouchEvent(ev);}//重寫回調方法,使左右菜單進行滑動@Overridepublic void computeScroll() {super.computeScroll();if (mScroller .computeScrollOffset()) {return;}int tempX=mScroller .getCurrX();scrollTo(tempX, 0);}private Point point =new Point(); //獲取當前屏幕的點,通過點來計算滑動的距離,根據滑動的距離來判斷相應的事件是滑動還是點擊private static final int TEST_DIS=20; //如果移動距離大于20就定義為滑動//判斷處理事件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 );//獲取水平方向上移動距離的絕對值int dY=Math.abs(( int)ev.getY()-point .y );//獲取垂直方向上移動距離的絕對值if (dX>=TEST_DIS && dX>dY) {//左右滑動isleftrightEvent=true ;isTestCompete=true ; //允許進入接下來的返出測試階段//獲取當前點的 xy坐標point. x=( int) ev.getX();//floatpoint. y=( int) ev.getY();} else if (dY>=TEST_DIS && dY>dX) { //上下滑動isleftrightEvent=false ;isTestCompete=true ;//允許進入接下來的返出測試階段point. x=( int) ev.getX();//floatpoint. y=( int) ev.getY();}break;case MotionEvent.ACTION_UP:case MotionEvent.ACTION_CANCEL://觸摸到屏幕的邊緣//對點擊事件進行處理(返還給系統處理)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;}
}
總結
以上是生活随笔 為你收集整理的Android项目开发实战—自定义左右菜单 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。