android 自定義ViewGroup之浪漫求婚

1、最終效果


這里寫圖片描述

有木有發(fā)現(xiàn)還是很小清新的感覺O(∩_∩)O~

2抬探、看整體效果這是一個scrollView酌壕,滑動時每個子view都有一個或多個動畫效果卵牍,但是如果我們直接給每個子view加上動畫去實現(xiàn)這個需求就太low了辛掠,而且也不利于擴展,所以這里將會設(shè)計一套框架猩谊,使別人能很方便的使用我們定義的控件牌捷。

3、首先看看我們是怎么使用自己設(shè)計的這個控件的

<scrollviewgroup.lly.com.scrollviewgroup.lib.DiscrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:discrollve="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <scrollviewgroup.lly.com.scrollviewgroup.lib.DiscrollViewContent
        android:layout_width="match_parent"
        android:layout_height="match_parent">

       ...

        <ImageView
            android:layout_width="300dp"
            android:layout_height="180dp"
            android:layout_gravity="center"
            discrollve:discrollve_alpha="true"
            discrollve:discrollve_translation="fromLeft|fromBottom"
            android:src="@drawable/cheese1" />

       ...

    </scrollviewgroup.lly.com.scrollviewgroup.lib.DiscrollViewContent>
</scrollviewgroup.lly.com.scrollviewgroup.lib.DiscrollView>

看discrollve:discrollve_alpha="true" discrollve:discrollve_translation="fromLeft|fromBottom"
這里我們給系統(tǒng)控件加上自定義屬性棒口,這樣當(dāng)別人用我們的控件剥懒,簡直不要太爽。

不過大家有沒有發(fā)現(xiàn)這是系統(tǒng)控件哎,你就這么隨隨便便的給它加個屬性崔梗,它認(rèn)識么 不報錯你就謝天謝地了 還讓它工作蒜魄,想的美旅挤。

帶著這個疑惑,我們先來看看系統(tǒng)的ViewGroup.Java類是怎么做的柒瓣。
一般我們在代碼中給布局動態(tài)添加子控件的時候都會用到addView這個方法 這里我們就跟蹤這個方法,最后發(fā)現(xiàn)他們會調(diào)用到ViewGroup的addview方法

public void addView(View child, int index) {
        LayoutParams params = child.getLayoutParams();
        if (params == null) {
            params = generateDefaultLayoutParams();
            if (params == null) {
                throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
            }
        }
        addView(child, index, params);
    }

有沒有發(fā)現(xiàn)這里這里最后的params是怎么來的 不就是子控件的params么。 而addView(child, index, params); 最后會調(diào)用addViewInner 下面我們看下addViewInner是怎么做的

private void addViewInner(View child, int index, LayoutParams params,
            boolean preventRequestLayout) {

        ...
        if (!checkLayoutParams(params)) {
            params = generateLayoutParams(params);
        }

        if (preventRequestLayout) {
            child.mLayoutParams = params;
        } else {
            child.setLayoutParams(params);
        }

       ...
        addInArray(child, index);

        // tell our children
        if (preventRequestLayout) {
            child.assignParent(this);
        } else {
            child.mParent = this;
        }

          ...
        onViewAdded(child);

        ...
    }

代碼還是比較多的,只關(guān)注對我們有用的片段 首先它會調(diào)用checkLayoutParams(params)

 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
        return  p != null;
    }

如果不等于空就會調(diào)用就調(diào)用generateLayoutParams

protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
        return p;
    }

繼續(xù)執(zhí)行

  if (preventRequestLayout) {
            child.mLayoutParams = params;
        } else {
            child.setLayoutParams(params);
        }

看到上面的checkLayoutParams和generateLayoutParams方法都比較簡單而且是protected的 所以應(yīng)該是給子類實現(xiàn)的,我們看一個viewgroup的子類 LinearLayout是怎么做的

@Override
    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
        return p instanceof LinearLayout.LayoutParams;
    }
        @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new LinearLayout.LayoutParams(getContext(), attrs);
    }

看到這里就在想我們是不是也可以這么做呢,那當(dāng)然是可以的 系統(tǒng)都可以了還有什么問題甫何, 接下來我們的大波代碼來襲了

public class DiscrollViewContent extends LinearLayout {

    public DiscrollViewContent(Context context) {
        super(context);
        setOrientation(VERTICAL);
    }

    public DiscrollViewContent(Context context, AttributeSet attrs) {
        super(context, attrs);
        setOrientation(VERTICAL);
    }

    public DiscrollViewContent(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setOrientation(VERTICAL);
    }

    /**
     * 重寫addView
     * @param child
     * @param index
     * @param params
     */
    @Override
    public void addView(View child, int index, ViewGroup.LayoutParams params) {
        super.addView(asDiscrollvable(child,(MyLayoutParams)params), index, params);
    }

    private View asDiscrollvable(View child, MyLayoutParams params) {
        if(!isDiscrollvable(params)){
            return child;
        }
        DiscrollvableView discrollvableChild = new DiscrollvableView(getContext());
        discrollvableChild.setDiscrollveAlpha(params.mDiscrollveAlpha);
        discrollvableChild.setDiscrollveTranslation(params.mDiscrollveTranslation);
        discrollvableChild.setDiscrollveScaleX(params.mDiscrollveScaleX);
        discrollvableChild.setDiscrollveScaleY(params.mDiscrollveScaleY);
        discrollvableChild.setDiscrollveThreshold(params.mDiscrollveThreshold);
        discrollvableChild.setDiscrollveFromBgColor(params.mDiscrollveFromBgColor);
        discrollvableChild.setDiscrollveToBgColor(params.mDiscrollveToBgColor);
        discrollvableChild.addView(child);
        return discrollvableChild;
    }

    /**
     * 判斷是否是我們定義的LayoutParams
     * @param lp
     * @return
     */
    private boolean isDiscrollvable(MyLayoutParams lp) {
        return lp.mDiscrollveAlpha ||
                lp.mDiscrollveTranslation != -1 ||
                lp.mDiscrollveScaleX ||
                lp.mDiscrollveScaleY ||
                (lp.mDiscrollveFromBgColor != -1 && lp.mDiscrollveToBgColor != -1);
    }

    /**
     * 重寫checkLayoutParams
     * @param p
     * @return
     */
    @Override
    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
        return p instanceof MyLayoutParams;
    }

    /**
     * 重寫generateDefaultLayoutParams
     * @return
     */
    @Override
    protected LinearLayout.LayoutParams generateDefaultLayoutParams() {
        return new MyLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
    }

    /**
     * 重寫generateLayoutParams
     * @param attrs
     * @return
     */
    @Override
    public LinearLayout.LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MyLayoutParams(getContext(), attrs);
    }

    /**
     * 重寫generateLayoutParams
     * @param p
     * @return
     */
    @Override
    protected LinearLayout.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
        return new MyLayoutParams(p.width, p.height);
    }

    /**
     * 自定義LinearLayout.LayoutParams
     */
    class MyLayoutParams extends LinearLayout.LayoutParams {

        private int mDiscrollveFromBgColor;
        private int mDiscrollveToBgColor;
        private float mDiscrollveThreshold;
        public boolean mDiscrollveAlpha;
        public boolean mDiscrollveScaleX;
        public boolean mDiscrollveScaleY;
        private int mDiscrollveTranslation;
        public MyLayoutParams(Context context, AttributeSet attrs) {
            super(context, attrs);
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DiscrollView_LayoutParams);
            try {
                mDiscrollveAlpha = a.getBoolean(R.styleable.DiscrollView_LayoutParams_discrollve_alpha, false);
                mDiscrollveScaleX = a.getBoolean(R.styleable.DiscrollView_LayoutParams_discrollve_scaleX, false);
                mDiscrollveScaleY = a.getBoolean(R.styleable.DiscrollView_LayoutParams_discrollve_scaleY, false);
                mDiscrollveTranslation = a.getInt(R.styleable.DiscrollView_LayoutParams_discrollve_translation, -1);
                mDiscrollveThreshold = a.getFloat(R.styleable.DiscrollView_LayoutParams_discrollve_threshold, 0.0f);
                mDiscrollveFromBgColor = a.getColor(R.styleable.DiscrollView_LayoutParams_discrollve_fromBgColor, -1);
                mDiscrollveToBgColor = a.getColor(R.styleable.DiscrollView_LayoutParams_discrollve_toBgColor, -1);
            } finally {
                a.recycle();
            }
        }

        public MyLayoutParams(int width, int height) {
            super(width, height);
        }

    }
}

上面這大段代碼主要就做了我們上面分析的系統(tǒng)空間

首先繼承LinearLayout, 重寫了addView亲族,generateLayoutParams,checkLayoutParams 并自定義了一個MyLayoutParams繼承自LinearLayout.LayoutParams 在addview的時候我們首先對child進行下處理,判斷子view中是否有我們定義屬性杏慰,沒有的話缘滥,就用它自己霎肯,有的話搂捧,我們在外層包一個FrameLayout,讓他執(zhí)行動畫,他的子view也將跟著執(zhí)行弱睦。

下面就是動畫的實現(xiàn)了

首先看我們的scrollView是怎么做的

 private void onScrollChanged(int top) {
        int scrollViewHeight = getHeight();
        int scrollViewBottom = getAbsoluteBottom();
        int scrollViewHalfHeight = scrollViewHeight / 2;
        for(int index = 1;index<mContent.getChildCount();index++){
            View child = mContent.getChildAt(index);
            if(!(child instanceof DiscrollVable)){
                continue;
            }
            DiscrollVable discrollvable = (DiscrollVable) child;
            int discrollvableTop = child.getTop();
            int discrollvableHeight = child.getHeight();
            int discrollvableAbsoluteTop = discrollvableTop - top;
            //這個view的下半部分
            if(scrollViewBottom - child.getBottom() < discrollvableHeight+scrollViewHalfHeight){
                //子view顯示的時候執(zhí)行
                if(discrollvableAbsoluteTop <= scrollViewHeight){
                    int visibleGap = scrollViewHeight - discrollvableAbsoluteTop;
                    discrollvable.onDiscrollve(clamp(visibleGap / (float)discrollvableHeight,0.0f,1.0f));
                }else {
                    //子view還沒顯示的時候
                    discrollvable.onResetDiscrollve();
                }
            }else{
                if(discrollvableAbsoluteTop <= scrollViewHalfHeight){
                    int visibleGap = scrollViewHalfHeight - discrollvableAbsoluteTop;
                    discrollvable.onDiscrollve(clamp(visibleGap / (float)discrollvableHeight,0.0f,1.0f));
                }else{
                    discrollvable.onResetDiscrollve();
                }
            }
        }
    }

主要就是在滑動的時候 把滑動的百分比傳給接口 ,具體由接口的實現(xiàn)類來執(zhí)行 而實現(xiàn)接口的類就是我們上面的那個FrameLayout仑嗅。

@Override
    public void onDiscrollve(float ratio) {
        if(ratio >= mDiscrollveThreshold) {
            ratio = withThreshold(ratio);
            float ratioInverse = 1 - ratio;

            if(mDiscrollveAlpha) {
                setAlpha(ratio);
            }
            if(isDiscrollveTranslationFrom(TRANSLATION_FROM_BOTTOM)) {
                setTranslationY(mHeight * ratioInverse);
            }
            if(isDiscrollveTranslationFrom(TRANSLATION_FROM_TOP)) {
                setTranslationY(-mHeight * ratioInverse);
            }
            if(isDiscrollveTranslationFrom(TRANSLATION_FROM_LEFT)) {
                setTranslationX(-mWidth * ratioInverse);
            }
            if(isDiscrollveTranslationFrom(TRANSLATION_FROM_RIGHT)) {
                setTranslationX(mWidth * ratioInverse);
            }
            if(mDiscrollveScaleX) {
                setScaleX(ratio);
            }
            if(mDiscrollveScaleY) {
                setScaleY(ratio);
            }
            if(mDiscrollveFromBgColor != -1 && mDiscrollveToBgColor != -1) {
                setBackgroundColor((Integer) sArgbEvaluator.evaluate(ratio, mDiscrollveFromBgColor, mDiscrollveToBgColor));
            }
        }

    }

    @Override
    public void onResetDiscrollve() {
        if(mDiscrollveAlpha) {
            setAlpha(0.0f);
        }
        if(isDiscrollveTranslationFrom(TRANSLATION_FROM_BOTTOM)) {
            setTranslationY(mHeight);
        }
        if(isDiscrollveTranslationFrom(TRANSLATION_FROM_TOP)) {
            setTranslationY(-mHeight);
        }
        if(isDiscrollveTranslationFrom(TRANSLATION_FROM_LEFT)) {
            setTranslationX(-mWidth);
        }
        if(isDiscrollveTranslationFrom(TRANSLATION_FROM_RIGHT)) {
            setTranslationX(mWidth);
        }
        if(mDiscrollveScaleX) {
            setScaleX(0.0f);
        }
        if(mDiscrollveScaleY) {
            setScaleY(0.0f);
        }
        if(mDiscrollveFromBgColor != -1 && mDiscrollveToBgColor != -1) {
            setBackgroundColor(mDiscrollveFromBgColor);
        }

    }

代碼貼的太多了 底部將給出源碼
可以看出 每個類都不是很大,當(dāng)用戶要用的時候只要 在xml中引用我們的控件,就可以實現(xiàn)這個效果羡亩,而且他要別的效果的話同樣只要在xml中配置就好。

github

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蛋勺,一起剝皮案震驚了整個濱河市贼陶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌眨层,老刑警劉巖酪捡,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件捺疼,死亡現(xiàn)場離奇詭異卧秘,居然都是意外死亡惕蹄,警方通過查閱死者的電腦和手機遭顶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進店門嗦哆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事旁舰⊙苄龋” “怎么了竹捉?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵憾儒,是天一觀的道長。 經(jīng)常有香客問我警儒,道長,這世上最難降的妖魔是什么变姨? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任砍鸠,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘弟断。我一直安慰自己冲秽,他們只是感情好排霉,可當(dāng)我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布冒滩。 她就那樣靜靜地躺著,像睡著了一般篇恒。 火紅的嫁衣襯著肌膚如雪智蝠。 梳的紋絲不亂的頭發(fā)上寻咒,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天,我揣著相機與錄音,去河邊找鬼员凝。 笑死瓶蚂,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的疤坝。 我是一名探鬼主播,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼乖订,長吁一口氣:“原來是場噩夢啊……” “哼哥遮!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起仪召,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎玖瘸,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年灭忠,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡旭旭,死狀恐怖源梭,靈堂內(nèi)的尸體忽然破棺而出仲闽,到底是詐尸還是另有隱情,我是刑警寧澤社牲,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站藤巢,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一澈圈、第九天 我趴在偏房一處隱蔽的房頂上張望努潘。 院中可真熱鬧报慕,春花似錦菌瘫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春苗胀,著一層夾襖步出監(jiān)牢的瞬間菇用,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工滤港, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人聘芜。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓口叙,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子启具,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內(nèi)容