天天看點

Android自定義元件(一)

 <b>Android</b><b>自定義元件(一)</b>

         在原生元件上避免不了覆寫、組合等,以定義自己的元件,也友善以後複用。例如之前工程裡出現了多次的檔案浏覽器元件。

         嗯~,該怎麼總結呢?

<b>一、概述</b>

         自定義元件,大概可以這麼分吧。一、View或SurfaceView上自繪;二、ViewGroup布局子類整合;三、不清楚了~,好像也沒什麼好分的==。

         本文的工程,個人覺着主要還是屬性資源的使用吧?工程主要例子介紹如下:

<b>名稱</b>

<b>效果</b>

<b>屬性</b>

Loading動态...的效果元件

loading...的動态效果

定義了如下四屬性:

1)loadImage:load字圖檔,reference類型

2)pointImage:小點圖檔,reference類型

3)pointCount:小點數目,integer類型

4)msecRate:毫秒級變化速率,integer類型

Title背景移位的效果元件

集合View布局,形成标題欄。實作了标題項下的背景移動的小效果。

定義了如下屬性:

1)titleLayout:标題欄布局

2)bgImage:item背景圖檔

3)bgLeftMargin:背景初始左邊距

4)animTime:移動動畫時間

ViewPager綁定标題的效果元件

ViewPager綁定标題欄,并實作了标題項下的背景移動的小效果。

效果特征如下:

1)背景随ViewPager滾動而同步在标題間滾動

2)點選标題時,ViewPager程式控制滾動&amp;背景同步

屬性定義如下:

1)tLayout:标題欄布局

2)bImage:item背景圖檔

3)bMargin:背景初始左邊距

ListView增加抽屜的效果元件

ListView增加抽屜的效果元件。抽屜打開的界面隻用了一個。

1)listViewId:清單視圖id,reference類型

2)drawerContent:抽屜内容視圖id,reference類型

3)drawerClose:抽屜内容的關閉按鈕id,reference類型

自定義能隐藏更多标題的元件

集合View布局,形成标題欄。實作超過标題數限制時,自動顯示更多的效果。

初始化時,需要進行如下步驟:

1)設定顯示數限制,預設将為6。

2)綁定标題内容。為String[],将直接以TextView顯示==

3)綁定更多操作的視圖id。将自己加載,并為其設定點選事件。

4)綁定更多顯示的視圖。應為已有的ViewGroup。将自動加載超出限制的标題内容(TextView)。更多操作則将控制其顯示或隐藏。

另外,提供重新整理内容的方法,用于:一、标題欄内容的重新加載;二,更多顯示内容的重新加載。

自繪實時動态資料線

利用View繪制的實時資料顯示元件?

寫該文檔時才挪進來的了,感覺弄得亂亂的。

雙點縮放好像很不正确啊?應該是兩個觸摸點沒弄對,獲得的是一個手指頭觸發的兩個點,是以一下放大了。(猜測,總之我是不修了^^)

         以下将以“ViewPager擴充元件”為例了,順便能看下ViewPager元件^^。

<b>二、步驟</b>

         帶屬性資源,整合布局建構自定義元件的步驟~

<b>1</b><b>)attrs.xml</b>

         定義元件需要用的屬性。不用的話,就相當于一個類為一個自定義元件,不和這些東西挂鈎。“自定義能隐藏更多标題的元件”即是這樣的,連屬性都沒定義==。

ViewPager擴充元件定義的内容:

&lt;!-- TitleViewPager --&gt; 

&lt;declare-styleable name="TitleViewPager"&gt; 

    &lt;attr format="reference" name="tLayout" /&gt; 

    &lt;attr format="reference" name="bImage" /&gt; 

    &lt;attr format="integer" name="bMargin" /&gt; 

&lt;/declare-styleable&gt; 

<b>2</b><b>)item.xml</b>

         隻要是xml的resources标簽内即可,單獨弄出來呢最好。用以下方式定義一個id,用于View.setId(int id),主要用于相對布局時,相對于某個id的View什麼的。

&lt;item name="containerLayout" type="id"/&gt; 

<b>3</b><b>)建立元件</b>

         其類中引用屬性資源。并看下ViewPager的使用說明吧:OnPageChangeListener接口方法和PagerAdapter擴充卡内方法的注釋。<b></b>

public class TitleViewPager extends RelativeLayout implements 

        OnPageChangeListener, View.OnClickListener { 

    private Context mContext; // 上下文 

    private LayoutInflater mInflater; // 布局加載器 

    private int titleLayoutId; // 标題欄布局id 

    private int bgImageResId; // item背景圖檔資源id 

    private int bgLeftMargin; // 背景初始左邊距 

    private View titleLayout; // 标題欄布局 

    private ImageView mBgImage; // item背景圖檔 

    private ArrayList&lt;View&gt; mItemViews; // 标題項視圖集合 

    private ViewPager mViewPager; // ViewPager元件 

    private ArrayList&lt;View&gt; mPageViews; // 頁面視圖集合 

    private int prevOffset = -1; // 前次偏移值,這裡用了int值像素 

    private int currentIndex; // 目前頁面索引 

    private int previousIndex; // 前次頁面索引 

    private boolean isTitleClicked; // 标題項點選 

    private OnPageChangeListener mOnPageChangeListener; // 頁面變化監聽事件 

    // private final int REFRESH_RATE = 20; // 重新整理速率20msec 

    // private Scroller mScroller; // 滾動器 

    // private static final Interpolator sInterpolator = new Interpolator() { 

    // public float getInterpolation(float t) { 

    // t -= 1.0f; 

    // return t * t * t + 1.0f; 

    // } 

    // }; 

    public TitleViewPager(Context context, AttributeSet attrs) { 

        super(context, attrs); 

        mContext = context; 

        mInflater = (LayoutInflater) context 

                .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 

        // 獲得TypedArray對象 

        TypedArray typedArray = context.obtainStyledAttributes(attrs, 

                R.styleable.TitleViewPager); 

        // 擷取标題欄布局id,預設0 

        titleLayoutId = typedArray.getResourceId( 

                R.styleable.TitleViewPager_tLayout, 0); 

        // 擷取item背景圖檔資源id,預設0 

        bgImageResId = typedArray.getResourceId( 

                R.styleable.TitleViewPager_bImage, 0); 

        // 擷取背景初始左邊距,預設0 

        bgLeftMargin = typedArray.getInt(R.styleable.TitleViewPager_bMargin, 0); 

        initLayout(); // 初始化标題欄&amp;ViewPager 

        mItemViews = new ArrayList&lt;View&gt;(); 

        mPageViews = new ArrayList&lt;View&gt;(); 

    } 

    // 初始化标題欄&amp;ViewPager 

    private void initLayout() { 

        RelativeLayout containerLayout = new RelativeLayout(mContext); // 建立标題欄容器布局 

        containerLayout.setId(R.id.containerLayout); // 設定辨別符 

        LayoutParams containerParams = new LayoutParams( 

                LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT); // 寬度WRAP_CONTENT,高度WRAP_CONTENT 

        containerParams.addRule(RelativeLayout.ALIGN_PARENT_TOP, 

                RelativeLayout.TRUE); // 貼于頂部 

        containerParams.addRule(RelativeLayout.CENTER_HORIZONTAL, 

                RelativeLayout.TRUE); // 水準居中 

        addView(containerLayout, containerParams); // 目前布局增加容器布局 

        if (0 != bgImageResId) { 

            mBgImage = new ImageView(mContext); // 建立item背景圖檔 

            mBgImage.setImageResource(bgImageResId); // 設定item背景圖檔 

            LayoutParams imageParams = new LayoutParams( 

                    LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); // 寬度WRAP_CONTENT,高度WRAP_CONTENT 

            imageParams.addRule(RelativeLayout.CENTER_VERTICAL, 

                    RelativeLayout.TRUE); // 垂直居中 

            imageParams.leftMargin = bgLeftMargin; // 左邊距 

            containerLayout.addView(mBgImage, imageParams); // 标題欄容器增加标題item背景圖檔 

        } 

        if (titleLayoutId != 0) { 

            titleLayout = mInflater.inflate(titleLayoutId, this, false); // 獲得标題欄布局 

            LayoutParams titleParams = new LayoutParams( 

                    LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT); // 寬度WRAP_CONTENT,高度WRAP_CONTENT 

            titleParams.addRule(RelativeLayout.CENTER_HORIZONTAL, 

                    RelativeLayout.TRUE); // 水準居中 

            titleParams.addRule(RelativeLayout.CENTER_VERTICAL, 

            containerLayout.addView(titleLayout, titleParams); // 标題欄容器增加标題欄布局 

        mViewPager = new ViewPager(mContext); // 建立ViewPager 

        mViewPager.setAdapter(new MPagerAdapter()); // 設定ViewPager擴充卡 

        mViewPager.setOnPageChangeListener(this); // 設定頁面改變的監聽接口 

        LayoutParams viewPagerParams = new LayoutParams( 

                LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); // 寬度FILL_PARENT,高度FILL_PARENT 

        viewPagerParams.addRule(RelativeLayout.BELOW, containerLayout.getId()); // 布于标題欄容器下方 

        viewPagerParams.addRule(RelativeLayout.CENTER_HORIZONTAL, 

        addView(mViewPager, viewPagerParams); // 目前布局增加容器ViewPager 

    // 增加一個綁定頁面 

    public void addBindedPage(int pageViewId, int titleItemId) { 

        mPageViews.add(mInflater.inflate(pageViewId, this, false)); 

        View item = titleLayout.findViewById(titleItemId); 

        item.setOnClickListener(this); 

        mItemViews.add(item); 

    // 獲得頁面數量 

    public int getCount() { 

        return mPageViews.size(); 

    // 初始化頁面(需要在UI加載完後,可以覆寫onWindowFocusChanged()) 

    public void setPage(int index) { 

        setImagePosition(index); // 設定圖像至标題項位置 

        mViewPager.setCurrentItem(index, false); // 設定ViewPager目前頁面 

    // 設定目前頁面 

    public void setCurrentPage(int index, boolean isAnim) { 

        previousIndex = currentIndex; // 記錄前次頁面索引 

        currentIndex = index; // 設定目前頁面索引 

        mViewPager.setCurrentItem(index, isAnim); // 設定ViewPager目前頁面 

        // Title移動綁定在ViewPager的滾動事件内 

    // 設定圖像至标題項位置 

    private void setImagePosition(int index) { 

        if (null == mBgImage) { 

            return; 

        LayoutParams params = (RelativeLayout.LayoutParams) mBgImage 

                .getLayoutParams(); // 獲得圖檔布局 

        View item = mItemViews.get(index); // 标題項 

        // 注:UI加載完後getLeft()才有值 

        int targetLeftMargin = (int) (item.getLeft() + item.getWidth() / 2.0 - mBgImage 

                .getWidth() / 2.0); // 目标左邊距 

        // 位置未變時直接傳回,以避免多次setLayoutParams(...) 

        if (params.leftMargin == targetLeftMargin) { 

        params.leftMargin = targetLeftMargin; 

        mBgImage.setLayoutParams(params); // 使設定生效 

    // 設定圖像移動像素距離 

    private void moveImagePosition(int offset) { 

        params.leftMargin += offset; 

    /* 

     * 目前頁滾動時調用,無論是程式控制的平滑滾動還是使用者發起的觸摸滾動。 

     * arg0:第一個頁面目前顯示的位置索引。如果頁面偏移不是0,下一個頁面将會可見。 

     * arg1:表示第二個頁面位置偏移量的比例值,[0, 1)。(右側頁面所占螢幕百分比) 

     * arg2:表示第二個頁面位置偏移量的像素值。(右側頁面距右邊的像素值) 

     */ 

    @Override 

    public void onPageScrolled(int position, float positionOffset, 

            int positionOffsetPixels) { 

        // 判斷是否不在動畫,用positionOffsetPixels判斷判斷原因是: 

        // 1)position,在選中了頁面時就會改變,自動動畫時的不能判斷 

        // 2)positionOffset,動畫完變為0.0,但float不好直接等于判斷 

        // 3)positionOffsetPixels,動畫完變為0,int型^^ 

        if (positionOffsetPixels == 0) { 

            setImagePosition(position); 

            prevOffset = -1; 

            isTitleClicked = false; 

        // 剛移動時,記錄下該次值 

        if (prevOffset == -1) { 

            prevOffset = positionOffsetPixels; 

        int pageOffset = positionOffsetPixels - prevOffset; // 頁面偏移距離 

        prevOffset = positionOffsetPixels; 

        if (null != mBgImage) { 

            try { 

                if (pageOffset &lt; 0) { // 左-&gt;右 

                    int prevIndex, nextIndex; 

                    if (isTitleClicked) { 

                        prevIndex = previousIndex; 

                        nextIndex = currentIndex; 

                    } else { 

                        prevIndex = currentIndex; 

                        nextIndex = currentIndex - 1; 

                    } 

                    // 兩菜單項間的距離 

                    int itemDistance = mItemViews.get(prevIndex).getLeft() 

                            - mItemViews.get(nextIndex).getLeft(); 

                    // 圖檔偏移距離 

                    int imageOffset = pageOffset * itemDistance 

                            / mViewPager.getWidth(); 

                    // 設定圖像移動像素距離 

                    moveImagePosition(imageOffset); 

                } else if (pageOffset &gt; 0) { // 右-&gt;左 

                        nextIndex = currentIndex + 1; 

                    int itemDistance = mItemViews.get(nextIndex).getLeft() 

                            - mItemViews.get(prevIndex).getLeft(); 

                } 

            } catch (IndexOutOfBoundsException e) { 

                // 類似在中間左右左右的來回拖拽==,判斷還不夠啊T^T 

                setImagePosition(currentIndex); 

                isTitleClicked = false; 

            } 

        if (null != mOnPageChangeListener) { 

            mOnPageChangeListener.onPageScrolled(position, positionOffset, 

                    positionOffsetPixels); 

     * 當一個新頁面被選中時被調用。動畫不一定必須完成。 

     * arg0:新選中頁面的位置索引 

    public void onPageSelected(int position) { 

            mOnPageChangeListener.onPageSelected(position); 

     * 滾動狀态改變時調用。用于發現使用者何時開始拖動、頁面何時自動沉降到目前頁(使用者不拖動時)、或者何時完全停止/空閑。 

     * arg0:新的滾動狀态。SCROLL_STATE_DRAGGING、SCROLL_STATE_SETTLING、SCROLL_STATE_IDLE 

    public void onPageScrollStateChanged(int state) { 

        if (state == ViewPager.SCROLL_STATE_DRAGGING) { // 使用者拖動時 

            mOnPageChangeListener.onPageScrollStateChanged(state); 

    // 自定義的ViewPager擴充卡 

    private class MPagerAdapter extends PagerAdapter { 

        /* 

         * 移除一個給定位置的頁面。擴充卡有責任從它的容器中移除視圖,雖然這僅必須确認動作是在finishUpdate()後按時間完成的。 

         * arg0:容器視圖,從中将移除頁面。 

         * arg1:移除的頁面位置 

         * arg2:和instantiateItem(View, int)傳回的一樣的對象 

         */ 

        @Override 

        public void destroyItem(View arg0, int arg1, Object arg2) { 

            ((ViewPager) arg0).removeView(mPageViews.get(arg1)); 

         * 當顯示的界面完成變化後調用。在這裡,你應當必須確定所有的頁面已經真正的從容器中增加或删除。 

         * arg0:容器視圖,用于顯示擴充卡的頁面視圖 

        public void finishUpdate(View arg0) { 

        // 傳回可用界面的數量 

        public int getCount() { 

            return mPageViews.size(); 

         * 建立一個給定位置的界面。擴充卡有責任給這邊給出的容器增加一個視圖,雖然這僅必須确認動作是在finishUpdate()後按時間完成的。 

         * arg0:容器視圖,在裡面将顯示頁面。 

         * arg1:要被裝載的頁面位置 

         * Object:傳回一個展現新畫面的對象。這不必須是一個View,也可以是一些其他的頁面容器。 

        public Object instantiateItem(View arg0, int arg1) { 

            ((ViewPager) arg0).addView(mPageViews.get(arg1), 0); 

            return mPageViews.get(arg1); 

        // 是否是由對象生成的視圖 

        public boolean isViewFromObject(View arg0, Object arg1) { 

            return arg0 == (arg1); 

        // 恢複狀态 

        public void restoreState(Parcelable arg0, ClassLoader arg1) { 

        // 儲存狀态,傳回序列化對象 

        public Parcelable saveState() { 

            return null; 

         * 當顯示的頁面将要開始變化時調用 

        public void startUpdate(View arg0) { 

    public void onClick(View v) { 

        int size = mItemViews.size(); // 大小 

        for (int i = 0; i &lt; size; i++) { 

            if (mItemViews.get(i).getId() == v.getId()) { 

                isTitleClicked = true; 

                setCurrentPage(i, true); 

                break; 

    // 獲得标題項視圖集合 

    public ArrayList&lt;View&gt; getItemViews() { 

        return mItemViews; 

    // 獲得 頁面視圖集合 

    public ArrayList&lt;View&gt; getPageViews() { 

        return mPageViews; 

    // 設定頁面變化監聽事件 

    public void setOnPageChangeListener(OnPageChangeListener listener) { 

        mOnPageChangeListener = listener; 

<a target="_blank" href="http://vaero.blog.51cto.com/4350852/872763"> <b>Android</b><b>自定義元件(二)</b></a>

         附件工程,見(二)。

     本文轉自winorlose2000 51CTO部落格,原文連結:http://blog.51cto.com/vaero/872734,如需轉載請自行聯系原作者

繼續閱讀