Android開發之自訂局部導覽功能表

來源:互聯網
上載者:User

Android開發之自訂局部導覽功能表
如今,要實現導航功能方案有很多。比如:   1、用3.0+內建的Toolbar + Fragment導航。   2、用Tabhost實現導航。小弟學淺,就只用過這兩種方案實現導航。   但是這兩種方案都有一個很明顯的弊端:導航的位置太過於固定了。比如Toolbar的就只能在標題列處(ps:源碼修改大神跳過)。還有Tabhost,雖然自訂Tabhost比直接繼承TabActivity更加靈活,但是卻沒有選項切換動畫(ps:也許是我沒發現)。        有時候,我們僅僅是想在一個畫面的一角處,貼上一個導航,用於切換導航啊,屬性設定之內的。這個時候不管是Toolbar還是Tabhost都有些大材小用,心有餘而力不足的感覺了。比如所示:     最近剛好項目有這方面的需要,就查了點資料。發現原理其實挺簡單的,如:   上面幾個tab用Button或者TextView來做就行,反正能響應點擊就行。下面的ImageView用於切換動畫,比如預設是tab1,這個時候點擊了tab3,那麼下面的ImageView就從tab1移動到tab3並且停留。   原理講明白之後,接下來就是具體的實現了,一般這類需要都能有兩種方式實現: 用xml中實現用java代碼動態實現。  Xml介面與java代碼控制分離是Android開發的亮點,也是無數入門書籍的敲門磚,但是這種實現就有一種非常大的局限性:今天這個項目有3個tab,明天的項目有4個tab,這個時候需要去改xml不說,還要去改一些底層實現,比如對ImageView的寬度的壓縮等等。為了移植性和拓展性,我選擇了java代碼實現,直接subClass LinearLayout來實現。我只做了一些基本的操作,大家可以在My Code上添加自己的操作,比如給每一個tab添加selector,添加事件回調等等。先,我的最精簡實現:   中間的移動是有動畫效果的哈,不是直接點哪兒就出現在哪兒,太生硬了。     接下來講解具體的實現過程:     子類化LinearLayout,當然也可以選擇子類化其他ViewGroup,看個人愛好。  public class CustomMenu extends LinearLayout implements OnClickListener       在attrs.xml檔案中申明自訂xml屬性  <?xml version="1.0" encoding="utf-8"?> <resources>   <declare-styleable name="CustomMenu"> <attr name="buttonNumber" format="integer" /> <attr name="indexbitmap" format="reference" /> <attr name="buttonHeight" format="dimension" /> </declare-styleable>   </resources>   其中buttonNumber:導航的tab個數      indexbitmap:移動的圖片,就是下面那一橫線 buttonHeight:導航的高度       在xml布局檔案添加布局,layout_width與layout_height可以隨意使用match_parent、wrap_content、或者限定dp    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:custommenu="http://schemas.android.com/apk/res/com.example.fragmentdemo" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" >   <TextView android:layout_width="match_parent" android:layout_height="30dp" android:text="@string/hello_world" />   <com.example.fragmentdemo.fragmentmenu.CustomMenu android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" custommenu:buttonNumber="5" custommenu:buttonHeight="40dp" custommenu:indexbitmap="@drawable/a" >   </com.example.fragmentdemo.fragmentmenu.CustomMenu>   <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/hello_world" /> </LinearLayout>   為了突出隨意性,故意在上下添加了兩個TextView,layout_width與layout_height可以設定為match_parent、wrap_content或者30dp等等。在xml屬性中,我將導覽列的數量設定為5個,導覽列的高度為40dp,導航的移動圖片為drawable       在java代碼中首先讀取自訂的xml屬性值    private void readXML(Context context,AttributeSet attr){     TypedArray a = context.obtainStyledAttributes(attr, R.styleable.CustomMenu);     //讀取按鈕數量     buttonNumber = a.getInt(R.styleable.CustomMenu_buttonNumber, 4);     //讀取按鈕的高度     buttonHeight = (int) a.getDimension(R.styleable.CustomMenu_buttonHeight, 30);     //讀取圖片     int bitmapID = a.getResourceId(R.styleable.CustomMenu_indexbitmap, R.drawable.a);     bitmap = BitmapFactory.decodeResource(getResources(), bitmapID);     bitmap_width = bitmap.getWidth();     a.recycle(); }   注釋已經寫得很清楚了,就是用來讀取在xml中自訂的屬性,這兒注意buttonNumber、buttonHeight、bitmap、bitmap_width都是成員屬性。       添加幾個tab,個數是根據buttonNumber限制了的,還有tab的高度也是根據buttonHeight限制了的。    //設定本身為豎直方向     setOrientation(LinearLayout.VERTICAL);     //添加一個橫向的LinearLayout,高度為設定的高度     LayoutParams p = new LayoutParams(LayoutParams.MATCH_PARENT, buttonHeight);     LinearLayout linearLayout = new LinearLayout(context);     linearLayout.setOrientation(LinearLayout.HORIZONTAL);     linearLayout.setPadding(0, 0, 0, 0);     linearLayout.setGravity(Gravity.CENTER);     addView(linearLayout, p);     //向這個橫向的LinearLayout添加指定個Button     LayoutParams btn_p = new LayoutParams(LayoutParams.MATCH_PARENT, buttonHeight, 1);     for(int i = 0;i<buttonNumber;i++){         Button button = new Button(context);         button.setText("按鈕"+i);         button.setTextSize(15);         button.setBackgroundColor(getResources().getColor(R.color.defaultColor));         button.setId(ID+i);         button.setOnClickListener(this);         //添加到容器         button_container.add(button);         //添加到布局         linearLayout.addView(button, btn_p);     }   這兒首先添加一個橫向的LinearLayout用來添加tab,高度用使用者輸入的值,然後添加使用者指定數量的tab(Button),設定權重(weight)為1。在這兒我把Button的文字、背景顏色等都給了預設值,大家可以在xml中拓展,或者在代碼中暴露方法讓使用者佈建。這兒有一個ID,我給了預設值 private static final int ID = 0xcc33cc; 這是為了區分onClick事件,大家可以自己選擇區分方式,不過在這裡用ID是有好處的,後面我會介紹。       添加ImageView,暫時不做處理,因為Bitmap要因為tab的寬度來動態調整    imageView = new ImageView(context); LayoutParams iv_p = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); iv_p.setMargins(0, 5, 0, 0); addView(imageView,iv_p);   這裡只是放置一個ImageView,具體的內容要等到後面設定,因為內容是動態,在建構函式期間不能確定其寬高。       在onMearsure方法中,擷取本View的寬度與高度  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {     super.onMeasure(widthMeasureSpec, heightMeasureSpec);     //只執行這個方法一次     if(width==0 || height==0){                     //得到自身的高度與高度         width = MeasureSpec.getSize(widthMeasureSpec);         height = MeasureSpec.getSize(heightMeasureSpec);         //做其他的初始化         initial();     } }   大家都知道,onMeasure方法會根據傳入的參數確定控制項的大小。一般在這個方法做控制項的動態伸縮和子控制項的伸縮。在這裡,我只是簡單的得到了本控制項的寬度和高度。Width和height都是成員變數。這裡用了if語句是因為這個方法預設會執行兩次,原因呢大概是作為ViewGroup剛開始會繪製一次,填充子控制項後又會繪製一次,具體的不太清楚,大家可以查查其他資料。這裡用if限定只執行一次。然後在initial()方法中,做剩下的初始化部分。       做一些初始化操作  //如果圖片的寬度比按鈕的寬度大,則對圖片進行處理 if(bitmap_width>width/buttonNumber){     //縮小圖片     bitmap = dealBitmap(bitmap, (float) (width)/buttonNumber/bitmap_width); }   private Bitmap dealBitmap(Bitmap bitmap ,float bili) {     Matrix matrix = new Matrix();     matrix.postScale(bili, bili); // 長和寬放大縮小的比例     Bitmap resizeBmp = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);     return resizeBmp; }   如果圖片的寬度大於了每一個tab的寬度,那麼就對圖片進行縮放,預設是縮放到與tab等同寬度。大家可以自己定義這個縮放範圍。甚至可以通過設定回調介面暴露給外部設定。   //設定位移值 imageView_offset = (width/buttonNumber-bitmap_width)/2; //設定圖片 imageView.setImageBitmap(bitmap); //初始化圖片位置 initialImageViewOffset();   設定位移值,這個imageView_offset也是成員變數。目的是讓ImageView放在tab的正中間。原理類似於:   然後設定bitmap,因為這個時候bitmap的寬高已經確定了。然後調用initialImageViewOffset()方法將剛才確定的offset的值設定進去   private void initialImageViewOffset() {     //位移值大於0則進行圖片移動     if(imageView_offset>0){         Matrix matrix = new Matrix();         matrix.postTranslate(imageView_offset, 0);         imageView.setImageMatrix(matrix);     } }   這裡對offset進行的大於0的判斷,因為如上所說,如果bitmap的寬度大於tab的寬度,那麼就需要縮放到和tab一樣大,這個時候offset自然等於0,就避免了無用功。       添加單擊事件  public void onClick(View v) {     //從當前項移動到點擊項     moveImageView(cur_index, v.getId()-ID);     //賦值當前項     cur_index = v.getId() - ID; }   這裡就可以看出用id區分tab的好處了。為了更方便,首先貼出moveImageView的代碼   private void moveImageView(int start,int end){     //要移動的距離     int length = (2 * imageView_offset + bitmap_width) * (end - start);     //初始位置,預設的ImageView在離左邊的imageView_offset處。     int offset = (2 * imageView_offset + bitmap_width)*start;     Animation animation = new TranslateAnimation(offset, offset + length, 0, 0);     //動畫結束後,View停留在結束的位置     animation.setFillAfter(true);     animation.setDuration(300);     imageView.startAnimation(animation); }   這裡的start是指的當前的tab編號,編號是從0開始的,比如tab0、tab1、tab2。 end是指的你點擊的tab編號。例如一開始我就點了tab3,那麼start=0,end=3。然後我又點了tab2,那麼start=3,end=2。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.