Android高手进阶篇4-实现侧滑菜单框架,一分钟集成到项目中

先来看下面的这张效果图:

Android高手进阶篇4-实现侧滑菜单框架,一分钟集成到项目中

上面这张效果图是百度影音的,现在在Android上很流行,最初是由facebook自己实现的,而后各大应用有跟风之势,那么这种侧滑效果是如何实现的呢?

网上现在这种侧滑菜单的例子很对,也有开源的框架sliderMenu,而且可以定义很多样式,但大部分例子,都只是实现了这种类似效果,没有实现一种可移植的框架,仅仅是单页面效果而已,而且集成起来复杂,鉴于此,我自己实现了一套侧滑菜单的框架:

1、最常用的支持左右策划

2、多个页面切换也好不费力,页面切换的逻辑已经实现好了,集成进来,只需要关注自己项目的业务逻辑

3、支持多个页面集成

4、支持退出业务逻辑

 

先上我自己实现的效果图:

Android高手进阶篇4-实现侧滑菜单框架,一分钟集成到项目中

 

Android高手进阶篇4-实现侧滑菜单框架,一分钟集成到项目中

Android高手进阶篇4-实现侧滑菜单框架,一分钟集成到项目中

下面 说一下实现原理:

     布局文件采用FrameLayout, 在一个FrameLayout下有二个子布局,一个是菜单,另一个是LeftSliderLayout,而LeftSliderLayout下面可以放二个子布局:第一个是阴影布局(左边阴影),第二个是要拖动的内容。,当向右拖动LeftSliderLayout时,就显示露出菜单布局。而向左拖动LeftSliderLayout时,就覆盖菜单布局。

   1.FrameLayout的布局文件local_media_fragment.xml

<?xml version="1.0" encoding="utf-8"?>

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="fill_parent"

    android:layout_height="fill_parent" >



    <include android:id="@+id/main_layout_below" layout="@layout/main_layout_below" />



    <com.zhaoxufeng.leftsliderlayout.lib.LeftSliderLayout

        android:id="@+id/main_slider_layout"

        android:layout_width="fill_parent"

        android:layout_height="fill_parent" >





        <!-- Shadow Child -->

        <ImageView

                android:layout_width="15px"

                android:layout_height="fill_parent"

                android:contentDescription="@null"

                android:scaleType="fitXY"

                android:src="@drawable/main_side_shadow" />



        <!-- Main Child -->

        <include android:id="@+id/main_slider_main" layout="@layout/local_media" />





    </com.zhaoxufeng.leftsliderlayout.lib.LeftSliderLayout>



</FrameLayout>

 

上面 xml 中main_layout_below是对应的左边菜单Menu布局文件(这个布局文件是固定的),local_media是你要的拖动布局

2、LeftSliderLayout.java代码

package com.zhaoxufeng.leftsliderlayout.lib;



import android.content.Context;

import android.graphics.Rect;

import android.util.AttributeSet;

import android.util.Log;

import android.view.MotionEvent;

import android.view.VelocityTracker;

import android.view.View;

import android.view.ViewConfiguration;

import android.view.ViewGroup;

import android.widget.Scroller;



public class LeftSliderLayout extends ViewGroup {



    private static final String TAG = "LeftSliderLayout"  ;



    private Scroller mScroller;

    private VelocityTracker mVelocityTracker;





    /**

     * Constant value for touch state

     * TOUCH_STATE_REST : no touch

     * TOUCH_STATE_SCROLLING : scrolling

     */

    private static final int TOUCH_STATE_REST = 0;

    private static final int TOUCH_STATE_SCROLLING = 1;

    private int mTouchState = TOUCH_STATE_REST;

    

    /**

     * Distance in pixels a touch can wander before we think the user is scrolling

     */

    private int mTouchSlop;

    

    /**

     * Values for saving axis of the last touch event.

     */

    private float mLastMotionX;

    private float mLastMotionY;

    

    /**

     * Values for VelocityTracker to compute current velocity.

     * VELOCITY_UNITS in dp

     * mVelocityUnits in px

     */

    private static final int VELOCITY_UNITS = 1000;

    private int mVelocityUnits;	

    

    /**

     * The minimum velocity for determining the direction.

     * MINOR_VELOCITY in dp

     * mMinorVelocity in px

     */

    private static final float MINOR_VELOCITY = 150.0f;

    private int mMinorVelocity;								

    

    /**

     * The width of Sliding distance from left. 

     * And it should be the same with the width of the View below SliderLayout in a FrameLayout.

     * DOCK_WIDTH in dp

     * mDockWidth in px

     */

    private static final float SLIDING_WIDTH = 270.0f;

    private int mSlidingWidth;									

    

    /**

     * The default values of shadow.

     * VELOCITY_UNITS in dp

     * mVelocityUnits in px

     */

    private static final float DEF_SHADOW_WIDTH = 10.0f;		

    private int mDefShadowWidth;								



    /**

     * Value for checking a touch event is completed.

     */

    private boolean mIsTouchEventDone = false;				

    

    /**

     * Value for checking slider is open.

     */

    private boolean mIsOpen = false;						

    

    /**

     * Value for saving the last offset of scroller ’ x-axis.

     */

    private int mSaveScrollX = 0;							

    

    /**

     * Value for checking slider is allowed to slide.

     */

    private boolean mEnableSlide = true;					

    

    private View mMainChild = null;

    private OnLeftSliderLayoutStateListener mListener = null;



	/**

	 * Instantiates a new LeftSliderLayout.

	 *

	 * @param context the associated Context

	 * @param attrs AttributeSet

	 */

    public LeftSliderLayout(Context context, AttributeSet attrs) {

    	this(context, attrs, 0);

    }



	/**

	 * Instantiates a new LeftSliderLayout.

	 *

	 * @param context the associated Context

	 * @param attrs AttributeSet

	 * @param defStyle Style

	 */

    public LeftSliderLayout(Context context, AttributeSet attrs, int defStyle) {

    	super(context, attrs, defStyle);

        mScroller = new Scroller(context);

        mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();

        

        /**

         * Convert values in dp to values in px;

         */

        final float fDensity = getResources().getDisplayMetrics().density;

        mVelocityUnits = (int) (VELOCITY_UNITS * fDensity + 0.5f);

        mMinorVelocity = (int) (MINOR_VELOCITY * fDensity + 0.5f);

        mSlidingWidth = (int) (SLIDING_WIDTH * fDensity + 0.5f);

        mDefShadowWidth = (int) (DEF_SHADOW_WIDTH * fDensity + 0.5f);

    }

    

    @Override

	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

    	super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    	

    	// check Measure Mode is Exactly.

    	final int widthMode = MeasureSpec.getMode(widthMeasureSpec);

        if (widthMode != MeasureSpec.EXACTLY) {

            throw new IllegalStateException("LeftSliderLayout only canmCurScreen run at EXACTLY mode!");

        }

        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        if (heightMode != MeasureSpec.EXACTLY) {

        	throw new IllegalStateException("LeftSliderLayout only can run at EXACTLY mode!");

        }



        // measure child views

        int nCount = getChildCount();

        for (int i = 2; i < nCount; i++) {

        	removeViewAt(i);

        }

        nCount = getChildCount();

        if (nCount > 0) {

        	if (nCount > 1) {

        		mMainChild = getChildAt(1);

        		getChildAt(0).measure(widthMeasureSpec, heightMeasureSpec);

			} else {

				mMainChild = getChildAt(0);

			}

        	mMainChild.measure(widthMeasureSpec, heightMeasureSpec);

		}

        

        // Set the scrolled position 

        scrollTo(mSaveScrollX, 0);

    }

    

	@Override

	protected void onLayout(boolean changed, int l, int t, int r, int b) {

		final int nCount = getChildCount();

		if (nCount <= 0) {

			return;

		}

		

		// Set the size and position of Main Child

		if (mMainChild != null) {

			mMainChild.layout(

				l,

				t,

				l + mMainChild.getMeasuredWidth(),

				t + mMainChild.getMeasuredHeight());

		}

		

		// Set the size and position of Shadow Child

		if (nCount > 1) {

			int nLeftChildWidth = 0;

			View leftChild = getChildAt(0);

			ViewGroup.LayoutParams layoutParams = leftChild.getLayoutParams();

			if (layoutParams.width == ViewGroup.LayoutParams.FILL_PARENT

					|| layoutParams.width == ViewGroup.LayoutParams.MATCH_PARENT) {

				nLeftChildWidth = mDefShadowWidth;

			} else {

				nLeftChildWidth = layoutParams.width;

			}

			leftChild.layout(

					l - nLeftChildWidth,

					t,

					l,

					t + leftChild.getMeasuredHeight());

		}

	}

	

    @Override

	public void computeScroll() {

		if (mScroller.computeScrollOffset()) {

            Log.d(TAG,"computeScroll exeuted:" + "x:" + mScroller.getCurrX() + "Y:" + mScroller.getCurrY())  ;

			scrollTo(mScroller.getCurrX(), mScroller.getCurrY());

			postInvalidate();

		}

    }



    @Override

	public boolean onTouchEvent(MotionEvent event) { 

    	

    	int nCurScrollX = getScrollX();

    	

    	// check touch point is in the rectangle of Main Child

    	if (mMainChild != null

    			&& mTouchState != TOUCH_STATE_SCROLLING

    			&& mIsTouchEventDone) {

			Rect rect = new Rect();

			mMainChild.getHitRect(rect);

			if (!rect.contains((int)event.getX() + nCurScrollX, (int)event.getY())) {

				return false;

			}

		}

    	

    	if (mVelocityTracker == null) {

            mVelocityTracker = VelocityTracker.obtain();

        }



        mVelocityTracker.addMovement(event);

        

        final int action = event.getAction();

        final float x = event.getX();

        

        switch (action) {

        case MotionEvent.ACTION_DOWN: {

        	if (!mScroller.isFinished()) {

        		mScroller.abortAnimation();

        	}

        	

        	mIsTouchEventDone = false;

            mLastMotionX = x;

            break;

        }



        case MotionEvent.ACTION_MOVE: {

        	// check slider is allowed to slide.

        	if (!mEnableSlide) {

				break;

			}

        	

        	// compute the x-axis offset from last point to current point

            int deltaX = (int) (mLastMotionX - x);

            if (nCurScrollX + deltaX < getMinScrollX()) {

        		deltaX = getMinScrollX() - nCurScrollX;

        		mLastMotionX = mLastMotionX - deltaX;

			} else if (nCurScrollX + deltaX > getMaxScrollX()) {

        		deltaX = getMaxScrollX() - nCurScrollX;

        		mLastMotionX = mLastMotionX - deltaX;

			} else {

				mLastMotionX = x;

			}

        	

            // Move view to the current point

        	if (deltaX != 0) {

        		scrollBy(deltaX, 0);

			}

        	

        	// Save the scrolled position 

        	mSaveScrollX = getScrollX();

        	break;

        }



        case MotionEvent.ACTION_CANCEL: 

        case MotionEvent.ACTION_UP: {

        	

        	// check slider is allowed to slide.

        	if (!mEnableSlide) {

				break;

			}

        	

        	final VelocityTracker velocityTracker = mVelocityTracker;

        	velocityTracker.computeCurrentVelocity(mVelocityUnits);

        	

        	// Set open or close state, when get ACTION_UP or ACTION_CANCEL event.

        	if (nCurScrollX < 0) {

        		int velocityX = (int) velocityTracker.getXVelocity();

        		if (velocityX > mMinorVelocity) {

	            	scrollByWithAnim(getMinScrollX() - nCurScrollX);

	            	setState(true);

	            }

	            else if (velocityX < -mMinorVelocity) {

	            	scrollByWithAnim(-nCurScrollX);

	            	setState(false);

	            } else {

	            	if (nCurScrollX >= getMinScrollX() / 2) {

	            		scrollByWithAnim(- nCurScrollX);

						setState(false);

					} else {

						scrollByWithAnim(getMinScrollX() - nCurScrollX);

	            		setState(true);

					}

	            }

			} else {

				if (nCurScrollX > 0) {

					scrollByWithAnim(-nCurScrollX);

				}

				setState(false);

			}

        	

            if (mVelocityTracker != null) {

            	mVelocityTracker.recycle();

            	mVelocityTracker = null;

            }



            mTouchState = TOUCH_STATE_REST;

            mIsTouchEventDone = true;

            break;

        }

        

        }

        return true;

    }



    @Override

    public boolean onInterceptTouchEvent(MotionEvent ev) {

    	

        final int action = ev.getAction();

    	

		if (mListener != null && !mListener.OnLeftSliderLayoutInterceptTouch(ev)) {

			return false;

		}

        

        if ((action == MotionEvent.ACTION_MOVE)

      		  && (mTouchState != TOUCH_STATE_REST)) {

                 return true;

        }



        final float x = ev.getX();

        final float y = ev.getY();

        switch (action) {

        case MotionEvent.ACTION_DOWN:

                 mLastMotionX = x;

                 mLastMotionY = y;

                 mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING; 

                 break;



        case MotionEvent.ACTION_MOVE:

                 final int xDiff = (int) Math.abs(mLastMotionX - x);

                 if (xDiff > mTouchSlop) { 

                          if (Math.abs(mLastMotionY - y) / Math.abs(mLastMotionX - x) < 1)

                                   mTouchState = TOUCH_STATE_SCROLLING;

                }

                break;



        case MotionEvent.ACTION_CANCEL:

        case MotionEvent.ACTION_UP:

                 mTouchState = TOUCH_STATE_REST;

                 break;

        }

        return mTouchState != TOUCH_STATE_REST;

    }

    

    /**

     * With the horizontal scroll of the animation

     * 

     * @param nDx x-axis offset

     */

    void scrollByWithAnim(int nDx) {

    	if (nDx == 0) {

			return;

		}



        Log.d(TAG,"scrollByWithAnim:" + "x:" + (getScrollX() + "Y:" + Math.abs(nDx)))  ;

		mScroller.startScroll(getScrollX(), 0, nDx, 0,

				Math.abs(nDx));



		invalidate();

    }



    /**

     * Get distance of the maximum horizontal scroll

     * 

     * @return distance in px

     */

    private int getMaxScrollX() {

    	return 0;

    }

    

    /**

     * Get distance of the minimum horizontal scroll

     * @return distance in px

     */

    private int getMinScrollX() {

    	return -mSlidingWidth;

    }

    

    

    /**

     * Open LeftSlideLayout

     */

    public void open() {

        Log.d(TAG,"scroll by width:" + (getMinScrollX() - getScrollX()))  ;

    	if (mEnableSlide) {

            Log.d(TAG,"scroll by width:" + (getMinScrollX() - getScrollX()))  ;

            scrollByWithAnim(getMinScrollX() - getScrollX());

        	setState(true);

		}

    }





    /**

     * Close LeftSlideLayout

     */

    public void close() {

    	if (mEnableSlide) {

	    	scrollByWithAnim((-1) * getScrollX());

	    	setState(false);

    	}

    }

    

    /**

     * Determine whether LeftSlideLayout is open

     * 

     * @return true-open,false-close

     */

    public boolean isOpen() {

    	return mIsOpen;

    }

    

    /**

     * Set state of LeftSliderLayout

     * 

     * @param bIsOpen the new state

     */

    private void setState(boolean bIsOpen) {

    	boolean bStateChanged = false;

    	if (mIsOpen && !bIsOpen) {

			bStateChanged = true;

		} else if (!mIsOpen && bIsOpen) {

			bStateChanged = true;

		}

    	

    	mIsOpen = bIsOpen;

    	

    	if (bIsOpen) {

    		mSaveScrollX = getMaxScrollX();

		} else {

			mSaveScrollX = 0;

		}

    	

    	if (bStateChanged && mListener != null) {

			mListener.OnLeftSliderLayoutStateChanged(bIsOpen);

		}

    }

    

    /**

     * enable slide action of LeftSliderLayout 

     * 

     * @param bEnable

     */

    public void enableSlide(boolean bEnable) {

		mEnableSlide = bEnable;

	}

    

    /**

     * Set listener to LeftSliderLayout

     */

    public void setOnLeftSliderLayoutListener(OnLeftSliderLayoutStateListener listener) {

    	mListener = listener;

    }



    /**

     * LeftSliderLayout Listener

     *

     */

    public interface OnLeftSliderLayoutStateListener { 

        

    	/**

    	 * Called when LeftSliderLayout’s state has been changed.

    	 * 

    	 * @param bIsOpen the new state

    	 */

    	public void OnLeftSliderLayoutStateChanged(boolean bIsOpen);

    	

    	/**

    	 * Called when LeftSliderLayout has got onInterceptTouchEvent.

    	 * 

    	 * @param ev Touch Event

    	 * @return true - LeftSliderLayout need to manage the InterceptTouchEvent.

    	 *         false - LeftSliderLayout don't need to manage the InterceptTouchEvent.

    	 */

    	public boolean OnLeftSliderLayoutInterceptTouch(MotionEvent ev);

   }

}

 

   

LeftSliderLayout有一个Listener。它有二个函数,一个是LeftSliderLayout的打开与关闭的状态改变;另一个是InterceptTouchEvent的回调,主要解决的是在拖动内容中有要处理左右滑动的控件与LeftSliderLayout的左右滑动的事件有冲突,当它返回true时,LeftSliderLayout会处理左右滑动,当它返回false时,就不处理左右滑动的事件。

为了实现侧滑菜单框架,故实现了一个BaseActivity,其他Activity只需要继承这个Activity就行,


3、BaseActivity.java代码

package com.zhaoxufeng.leftsliderlayout.example;



import android.app.Activity;

import android.content.Intent;

import android.os.Bundle;

import android.util.Log;

import android.view.MotionEvent;

import android.view.View;

import android.widget.*;

import com.zhaoxufeng.leftsliderlayout.R;

import com.zhaoxufeng.leftsliderlayout.lib.LeftSliderLayout;



import java.util.ArrayList;

import java.util.List;



/**

 * 基类Activity,SliderMenu的基础统一框架

 * User: zhiwen.nan

 * Date: 13-10-7

 * Time: 下午8:31

 *

 */

public class BaseActivity extends Activity implements LeftSliderLayout.OnLeftSliderLayoutStateListener, View.OnClickListener {



    private  LeftSliderLayout leftSliderLayout;

    private ImageView mOpenButton;

    private TextView mTitleText;

    private ListView mListView;

    private List<ListItem> mDataList;

    private  long waitTime = 2000;

    private  long touchTime = 0;

    private static final  String TAG = "BaseActivity"  ;



    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

    }



    @Override

    protected void onResume() {

        super.onResume();

        bindView();

        initialDataList();

        ListViewAdapter listViewAdapter = new ListViewAdapter(BaseActivity.this,mDataList) ;

        mListView.setAdapter(listViewAdapter);

        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {

            @Override

            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {

                finish();

                switch (i)  {

                    case 0:

                        mTitleText.setText(getText(R.string.categ_local_video_list));

                        Intent intent = new Intent(BaseActivity.this,LocalMediaActivity.class)  ;

                        startActivity(intent);

                        break;

                    case 1:

                        mTitleText.setText(getText(R.string.cate_leida));

                        Intent radIntent = new Intent(BaseActivity.this,RadoActivity.class)  ;

                        startActivity(radIntent);

                        break;

                    case 2:

                        mTitleText.setText(getText(R.string.hot_viedo));

                        Intent hotIntent = new Intent(BaseActivity.this,HotMediaListActivity.class)  ;

                        startActivity(hotIntent);

                        break;

                    case 3:

                        mTitleText.setText(getText(R.string.cate_favrouite_list));

                        Intent collectIntent = new Intent(BaseActivity.this,CollectListActivity.class)  ;

                        startActivity(collectIntent);

                        break;

                    default:

                        leftSliderLayout.close();

                        break;



                }

            }

        });

    }



    @Override

    public void onClick(View view) {



    }



    @Override

    public void OnLeftSliderLayoutStateChanged(boolean bIsOpen) {



        if (bIsOpen) {

//            Toast.makeText(this, "LeftSliderLayout is open!", Toast.LENGTH_SHORT).show();

            Log.d(TAG," leftsilder is open")  ;

        } else {

           // Toast.makeText(this, "LeftSliderLayout is close!", Toast.LENGTH_SHORT).show();

            Log.d(TAG," leftsilder is close")  ;

        }



    }



    @Override

    public boolean OnLeftSliderLayoutInterceptTouch(MotionEvent ev) {



        return false;

    }



    private  void initialDataList(){

        mDataList = new ArrayList<ListItem>() ;

        for (int i = 0; i<= 3; i ++)       {

            ListItem listItem = new ListItem();

            listItem.setImageType(i);

            mDataList.add(listItem);



        }

    }



    private  void bindView(){

        leftSliderLayout = (LeftSliderLayout) findViewById(R.id.main_slider_layout);

        leftSliderLayout.setOnLeftSliderLayoutListener(this);

        mOpenButton = (ImageView)findViewById(R.id.openButton) ;

        mTitleText = (TextView)findViewById(R.id.titleText) ;

        mListView = (ListView)findViewById(R.id.listTab)  ;



        mOpenButton.setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View view) {

                if(leftSliderLayout.isOpen()) {

                    leftSliderLayout.close();

                } else {

                    leftSliderLayout.open();

                }



            }

        });



    }



    public  void openLeftSlider(boolean isToOpen){

        if(isToOpen)    {

            leftSliderLayout.open();

        }else {

            leftSliderLayout.close();

        }



    }





    public  void enableSlider(boolean isEnable)    {

         if(isEnable)  {

              leftSliderLayout.enableSlide(true);

         } else {

             leftSliderLayout.enableSlide(false);

         }

    }



    @Override

    public void onBackPressed() {

        if(!leftSliderLayout.isOpen())   {

            leftSliderLayout.open();

        } else {

            long currentTime = System.currentTimeMillis();

            if((currentTime-touchTime)>=waitTime) {

                Toast.makeText(this, "再按一次退出", Toast.LENGTH_SHORT).show();

                touchTime = currentTime;

            }else {

                finish();

                //todo

                //退出业务逻辑 ,根据项目需求来写

            }

        }



    }

}

 

关于左侧菜单的业务逻辑都在BaseActivity里处理,另外返回的逻辑也在里面处理,顶部统一的导航栏打开菜单栏业务逻辑,还有左侧菜单跳转的业务逻辑

4、LocalMediaActivity.java

package com.zhaoxufeng.leftsliderlayout.example;



import android.app.Activity;

import android.database.Cursor;

import android.os.Bundle;

import android.provider.Contacts;

import android.view.MotionEvent;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.*;

import com.zhaoxufeng.leftsliderlayout.R;

import com.zhaoxufeng.leftsliderlayout.lib.LeftSliderLayout;

import com.zhaoxufeng.leftsliderlayout.lib.LeftSliderLayout.OnLeftSliderLayoutStateListener;



import java.util.ArrayList;

import java.util.List;



/**

 * @author zhiwen.nan

 * @since 1.0

 * 本地视频界面

 */

public class LocalMediaActivity extends BaseActivity {



    private ListView mListView;

    private TextView mTitleText;



    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.local_media_fragment);



        mListView = (ListView)findViewById(R.id.localVideoList) ;



        mTitleText = (TextView)findViewById(R.id.titleText)  ;

        mTitleText.setText("本地视频");



        Cursor cursor = getContentResolver().query(Contacts.People.CONTENT_URI, null, null, null, null);

        startManagingCursor(cursor);

        ListAdapter listAdapter = new SimpleCursorAdapter(this, android.R.layout.simple_expandable_list_item_1,

                cursor,new String[]{Contacts.People.NAME},new int[]{android.R.id.text1});

        mListView.setAdapter(listAdapter);



    }





   

    @Override

    public boolean OnLeftSliderLayoutInterceptTouch(MotionEvent ev) {



        return true;

    }





}


 

LocalMediaActivity是自己定义的Activty和业务逻辑界面,只需继承BaseActivity即可,其他Activity类似。

以上就是核心代码,源代码下载:

http://download.csdn.net/detail/nanzhiwen666/6394347

 

 


 


 

 

你可能感兴趣的:(android)