編輯:關於Android編程
案例四 自定義開關:
功能介紹:本案例實現的功能是創建一個自定義的開關,可以自行決定開關的背景。當滑動開關時,開關的滑塊可跟隨手指移動。當手指松開後,滑塊根據開關的狀態,滑到最右邊或者滑到最左邊,同時保存開關的狀態,將開關的狀態回調給調用者。當然,上述功能系統給定的switch控件也可以實現。
實現步驟:
1. 寫一個類繼承view,重寫兩個參數的構造方法。在構造方法中指定工作空間,通過attrs.getAttributeResourceValue方法將java代碼中的屬性值和xml中的屬性值聯系起來。這樣可以在xml文件中指定相關的屬性值。重寫onmeasure和ondraw方法,繪制圖片。這裡測量圖片大小直接用setMeasuredDimension方法,獲取圖片本身的大小。
2. 設置接口回調。對於圖片來說,我們希望能夠在調用者獲取開關的狀態,因此需要設置一個接口回調,用於監控開關的狀態,當開關的狀態發生變化時間調用。接口回調的優勢在於調用者並不知道何時調用,所以在另一個文件中設置一個接口,在該文件觸發事件。由於重寫了接口的方法,因此,執行重寫後的方法。這樣就可以實現數據的回調。自定義控件中接口回調的應用較為廣泛,幾乎所有的控件都需要設置監聽,且寫法較為固定。
3. 重寫ontouchevent()方法。分析得知,開關由兩部分組成,一部分是底座兒,一部分是劃片而。當手指滑動時,劃片兒應該跟隨手指移動。當劃片的左邊小於底座左邊坐標時,讓劃片左邊的坐標和底座對齊,當劃片的右邊大於底座右邊坐標時,讓劃片右邊的坐標和底座對齊,這樣保證劃片不越界。當手指松開後,判斷劃片的中線坐標是在底座兒中線坐標的左邊還是右邊,以此來決定劃片最終是停在左邊還是右邊。同時改變開關的狀態,將開關的狀態回調給控件的調用中,讀取開關的狀態。
代碼實現。 主程序(調用者)中的代碼:
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 ? "開" : "關", Toast.LENGTH_SHORT).show(); } }); } }
自定義開關的具體實現;
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); } /** * 設置開關的圖片 * @param slideBackgroundResId 開關的背景圖片資源id * @param slideIconResId 開關上面的滑塊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; } /** 設置開關按鈕的狀態 */ public void setState(boolean state) { checkState(state); if (state) { slideIconLeft = maxSlideIconLeft; } else { slideIconLeft = 0; } } public void setOnStateChangedListener(OnStateChangedListener mOnStateChangedListener) { this.mOnStateChangedListener = mOnStateChangedListener; } /** 開關按鈕狀態改變的監聽器 */ public interface OnStateChangedListener { void onStateChanged(boolean state); } /** * 對View進行測量的方法 */ @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>
為了使布局文件中的屬性有作用,還要單獨在values文件夾中寫一個attrs.xml文件,聲明相關的屬性。
<?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:
功能介紹: 系統本身的Listview默認情況下是沒有下拉刷新和上拉加載更多的功能。但是Listview有addHeadview和addFootView方法,因此可以為listview加頭布局或者腳布局。對於頭布局來說,有三個狀態,下拉刷新、松開刷新、正在刷新,同時伴有相應的動畫予以提示。刷新成功後,為listview添加一套信息。同樣的腳布局加載成功後,在listview的最後一條數據後面,在加載一條信息。
實現步驟:
1. 寫布局文件。這裡除了在主界面中需要一個Listview外,還需要一個頭部的布局文件和一個尾部的布局文件。初始狀態時,頭布局和腳布局均是隱藏的。當滑動事件出發的時候,調整頭布局和腳布局的位置。這裡。頭布局和腳布局的根表簽最好是使用Linearlayout進行包裹,否則在滑動顯示是容易出現問題。
2. 寫一個類繼承listview。初始化界面布局,通過View.measure(0, 0)方法主動觸發測量,mesure內部會調用onMeasure,從而獲取到頭布局的高度。如步驟一所說,開始狀態時,頭布局是隱藏的,所以為頭布局設置panding值,來使得頭布局隱藏,只需要把setpadding中的第二個參數設置為負的頭文件的高。同樣的,顯示頭布局只需要把setpadding中的第二個參數設置為0。對於根布局的顯示和隱藏同樣采用上述的方法。當然也可以改變setpadding的第四個參數來控制顯示和隱藏。
3. 重寫OntouchEvent()方法。手指按下時,記錄下初始位置。手指滑動時在記錄一個y坐標。通過兩次坐標的差值,判斷手指滑動的方向。在可見的第一個條目的position為0的時候,如果手指向下滑動,有兩種狀態,一個是下拉刷新,指的是頭布局從不可見到剛好全部可見,一個是松開刷新,頭布局全部可見後繼續向下拖動,就會進入松開刷新狀態。另外還有一種狀態就是正在刷新,這種狀態下,頭布局剛好停留在頂部,維持一段時間。實際開發中,正在刷新是向服務器請求網絡。這裡添加的是假數據。不同的狀態之間的轉換還伴隨有動畫效果,因此,這裡還寫有補間動畫來實現向上和向下的效果。
4. 寫一個滑動監聽事件,在監聽事件中進行腳布局邏輯的編寫。當滿足以下三種情況的時候則顯示腳布局,一個ListView處於空閒狀態,另一個是界面上可見的最後一條item是ListView中最後的一條item,還有一個是如果當前沒有去做正在加載更多的事情。顯示腳布局的時候,不僅要設置padding值,同時選中listview最後一個條目,這樣就可以在界面上完整的顯示。
5. 最後要注意的是,無論是同布局,還是腳布局,都要對狀態進行修改。所以,可以增加一個回調監聽。表明數據刷新或者加載完成,可以隱藏頭布局和腳布局了,同時,頭布局的狀態恢復為下拉刷新,腳布局不是正在加載。同時,停掉相關的動畫,修改顯示的狀態。
主程序(調用者)的編寫。一般刷新是請求數據庫數據,這裡直接給出模擬數據演示效果。
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(); } }); } /** * 重新聯網獲取數據 */ protected void reloadData() { new Handler().postDelayed(new Runnable() { @Override public void run() { datas.add(0, "我是刷新出來的數據"); adapter.notifyDataSetChanged(); listView.onRefreshComplete(); } }, 3000); } /** * 聯網加載更多數據 */ protected void loadMore() { new Handler().postDelayed(new Runnable() { @Override public void run() { datas.add("我是加載更多出來的數據"); 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; // 默認是下拉刷新狀態 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); } /** * 設置顯示進度的圈圈 * * @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要清除動畫才能真正的隱藏 } } /** * 創建旋轉動畫 * * @param fromDegrees 從哪個角度開始轉 * @param toDegrees 轉到哪個角度 * @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); } /** * 設置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刷新的監聽器 */ public interface OnRefreshingListener { void onRefreshing(); void onLoadMore(); } /** * 聯網刷新數據的操作已經完成了 */ public void onRefreshComplete() { hideHeaderView(); currentState = STATE_PULL_TO_REFRESH; showRefreshingProgressBar(false); } /** * 加載更多新數據的操作已經完成了 */ 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" 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 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>
案例六 側滑菜單:
功能分析: 之前的案例大部分都只有一個控件,因此是通過繼承view實現。側滑菜單顯然需要兩個控件才能實現,一個用於加載主界面,一個用於加載側滑界面。所以這裡需要繼承viewgroup。通過在主界面滑動來控制界面的顯示和隱藏。
實現步驟:
1. 布局文件的書寫。布局文件包含兩個部分,主界面和側滑界面。主界面中有一個標題欄,標題欄有圖片按鈕,文字組成。圖片按牛同樣可以控制側邊欄的顯示和隱藏。側邊欄在ScrollView中添加一個Linearlayout,在linearlayout中縱向排列textview顯示文本。當然,側滑菜單同樣可以用listview顯示,這裡為簡單起見就不采用listview顯示。此外,TextView要想響應點擊事件,需要設置clickable為true。
2. 創建一個類繼承於ViewGroup。重寫onmeasure()方法,測量測量控件大小,包括兩個子控件。這裡,側滑菜單的寬在布局文件中寫好了,主界面的寬適配手機界面。所以分別調用menu.measure(menuWidth, heightMeasureSpec)和main.measure(widthMeasureSpec, heightMeasureSpec)方法即可獲取。排版容器中的子View。由於該自定義的控件包含不止一個子view,所以重寫onlayout()方法是必不可少的。對子View進行排版,子View的0,0坐標是SlidingMenu的左上角。對側滑菜單進行排版,菜單的left坐標在負的菜單寬的位置,菜單的top坐標在0的位置,菜單的right坐標在0的位置,菜單的bottom坐標在容器的最底邊。對主界面進行排版,主界面的left坐標在0的位置,主界面的top坐標在0的位置,主界面的right坐標在容器的最右邊,主界面的bottom坐標在容器的最底邊。
3. 重寫onInterceptTouchEvent。本案例中只關心水平劃動,所以如果水平移動距離比垂直移動距離大,則認為是水平移動,把事件攔截,不讓ScrollView使用,此事件交由控件本身處理。這裡有必要介紹一下事件分發。視圖集合對於事件的分發,自上而下處理。ViewGroup擁有下面3個Touch相關方法,dispatchTouchEvent(MotionEvent ev)用於Touch事件的頒發,onInterceptTouchEvent(MotionEvent ev) 用於攔截Touch事件,onTouchEvent(MotionEvent event) 用於處理Touch事件。這裡重寫了onInterceptTouchEvent(),相應的還要重寫ontouchevent()方法。
4. 在ontouchevent()方法中實現滑動跟隨的邏輯。滑動事件中,只關心橫向滑動。按下時,記錄下當前的x坐標。滑動時,再次記錄當前x坐標,兩者相減得到移動的距離。通過scrollTo()方法滑到相應的位置。系統給定的scrollTo()方法默認向右為負值,所以可重寫scrollTo()方法保證移動為正值。代碼優化。其實上述過程可以實現側滑菜單的功能。當滑動過程略顯生硬。這時就可以用到Scroller,這個類專門用於模擬滾動的數值。同時要配合computeScroll()方法使用。
5. 還有一個菜單的開關按鈕,要麼開,要麼關。開的時候完全顯示側滑菜單。關的時候,隱藏側滑菜單。這點邏輯和ontouchevent()中手指抬起的邏輯一樣。
主程序(調用者)的編寫:
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); } /** 設置當前選擇的菜單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); } } /** 菜單列表中的某個菜單項被單擊了 */ 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(); } }
側滑菜單的主體邏輯。
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; /** 這個類專門用於模擬滾動的數值 */ 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的位置,傳正數往右移,傳負往左移 * @param x */ public void scrollTo(int x) { super.scrollTo(-x, 0); } /** 獲取當前滑動到的位置 */ 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 要滑動到哪裡(目標位置) */ 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); } /** 菜單的開關按鈕,要麼開,要麼關 */ 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>
側滑界面布局
<?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="新聞" android:drawableLeft="@mipmap/tab_news"/> <TextView android:text="訂閱" android:drawableLeft="@mipmap/tab_read"/> <TextView android:text="本地" android:drawableLeft="@mipmap/tab_local"/> <TextView android:text="跟貼" android:drawableLeft="@mipmap/tab_ties"/> <TextView android:text="圖片" android:drawableLeft="@mipmap/tab_pics"/> <TextView android:text="話題" android:drawableLeft="@mipmap/tab_ugc"/> <TextView android:text="投票" android:drawableLeft="@mipmap/tab_vote"/> <TextView android:text="聚合閱讀" android:drawableLeft="@mipmap/tab_focus"/> </LinearLayout> </ScrollView>
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持本站。
我們常常聽說電腦重裝系統,那麼現在的智能手機可以重裝系統嗎?手機是可以重裝系統的,但是我們通常叫刷機,今天小編就為大家分享一下如何使用刷價軟件卓大師來進行刷
第十八章、代理模式 代理模式也稱委托模式,是結構型設計模式之一。是應用廣泛的模式之一。1.定義為其他對象提供一種代理以控制對這個對象的訪問。2.使用場景當無法或不想直接訪
在這篇文章中,我將通過不同的自動化工具如CheckStyle,FindBugs,PMD以及Android Lint來介紹(如何)提高你的安卓代碼質量。通過自動化的方式檢查
看了很多相關博客,今天也來自己梳理以下~~~Android從Linux系統啟動 init進程啟動 Native服務啟動 System Server, Android 服務