一塊兒擼個朋友圈吧 - 圖片瀏覽(上)【圖片點擊前景色】

項目地址:github.com/razerdp/Fri… (能弱弱的求個star或者fork麼QAQ)java


前言

終於,這個系列的文章進入了全新的篇章,我們的朋友圈系列進入研磨階段,目前咱們能夠成功展現數據,能夠進行點贊或者取消點贊,能夠進行評論。github

但完成這些基本功能是不夠的。canvas

一款產品之因此深刻人心,是由於 「用着舒服」,說是「用着舒服」倒不如說是 「看着舒服」,而視覺交互,或者說交互動畫,正是一款App打動人的最重要的地方。數組

在以前的文章裏,我其實一直很注重這些小細節的,而這些小細節,也是微信如今所擁有的,或許你日常沒怎麼留意,但假若取消掉這些動畫,相信你很快就會發覺 「這他喵的這麼生硬」瀏覽器

在本系列以前的文章裏,咱們有留意到並實現瞭如下的動畫:緩存

或許您想不到,一個小小的朋友圈看似簡單,卻有着這麼多小動畫。

好的,說了這麼多東西,除了總結外,實際上就是爲了推銷文章←_←,我不知道是否真的有人會看,也許會有人僅僅是爲了拿源碼伸伸手而已。

但,我認爲,跟同一圈子的人交流本身的思想,分享本身的經驗,不是一件很美妙的事情嗎?因此,即便沒什麼人看,我也會堅持把這個開源項目完成的以及在簡書堅持更新全部思路的-V-


正文

正如前言那一堆廢話所說,今天咱們要實現的是這麼一個效果:

preview - size:8.0M,流量黨請注意

如您所見,當咱們點擊圖片的時候,你會發現,圖片會有一個灰色的蒙層疊加在上面,看起來就像是咱們選中了圖片同樣。

若是要實現這個效果,按照咱們的平時習慣,確定是「selector走起」,奈何,當咱們真的去試了一下以後,發現,貌似無論用啊0.0

因而百度一番,或者谷歌一番,發現又是設置clickable啊,又是selector什麼亂七八糟的順序問題啊。。。。

與其執着於這些,倒不如我們自定義一個出來以應付一切的imageview。


關於Selector

若是硬要解釋這個東東,我想,我應該從新寫一篇文章來專門講解一下這個東東(事實上我也打算這麼作)

但在這裏我只會簡單的說說Selector究竟是如何實現view的視圖變化的:

  • 不管是什麼Selector,實際上最終都是Drawable,而Drawable能夠理解爲圖片,但若是須要更好的描述它,我以爲將其理解爲ps可能會更好,由於Drawable是一個抽象類,它提供了**「something that can be drawn」**的方法,其實弄來弄去,都是draw()方法

  • Selector在java中具體化的說,其實就是StateListDrawable。

  • Drawable有一個mState數組,它維護了不一樣狀態下的drawable,當view接收到touch事件,會調用refreshDrawableState來更新狀態,通常來講經過Drawable的isStateful()函數來得知是否與上次的狀態不一樣,若是是,則進行draw方法來改變view的視圖,在咱們的眼中看起來就是顏色的改變。

上面很簡單的講述了selector的實現過程,從中咱們不可貴到如下信息:

  • 維護不一樣狀態對應的drawable的數組
  • 根據狀態是否改變來獲得對應狀態的drawable
  • draw方法

實現

梳理了一遍過程以後,咱們只須要對症下藥就行了。

因而咱們正式開工:

首先仍是個人習慣,在自定義一個view以前,先配置attrs,在這裏咱們就只配置一個屬性用來改變前景色。

attrs:

<!--ForceClickImageView-->
    <declare-styleable name="ForceClickImageView">
        <attr name="foregroundColor" format="reference|color"/>
    </declare-styleable>
複製代碼

而後新建一個ForceClickImageView類,繼承本項目的SuperImageView(實際上就是普通的imageview,封裝了glide的加載方法)

/** * Created by 大燈泡 on 2016/4/11. * 朋友圈的imageview,包含點擊動做 */
public class ForceClickImageView extends SuperImageView {
    //前景層
    private Drawable mForegroundDrawable;
    private Rect mCachedBounds = new Rect();

    public ForceClickImageView(Context context) {
        this(context, null);
    }

    public ForceClickImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ForceClickImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }
   
}
複製代碼

咱們能夠看到,在這裏我加了兩個成員,一個天然是drawable,另外一個則是一個矩形,這個矩形主要是用來規定咱們的drawable繪製的範圍,在這裏咱們主要是用來緩存這個view的範圍。

接下來在初始化方法裏面咱們補充一下代碼:

/** * 初始化 */
    private void init(Context context, AttributeSet attrs) {
        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ForceClickImageView);
        mForegroundDrawable = a.getDrawable(R.styleable.ForceClickImageView_foregroundColor);
        if (mForegroundDrawable instanceof ColorDrawable) {
            int foreGroundColor = a.getColor(R.styleable.ForceClickImageView_foregroundColor, 0x55c6c6c6);
            mForegroundDrawable = new StateListDrawable();
            ColorDrawable forceDrawable = new ColorDrawable(foreGroundColor);
            ColorDrawable normalDrawable = new ColorDrawable(Color.TRANSPARENT);
            ((StateListDrawable) mForegroundDrawable).addState(new int[] { android.R.attr.state_focused },
                    forceDrawable);
            ((StateListDrawable) mForegroundDrawable).addState(new int[] { android.R.attr.state_pressed },
                    forceDrawable);
            ((StateListDrawable) mForegroundDrawable).addState(new int[] { android.R.attr.state_enabled },
                    normalDrawable);
            ((StateListDrawable) mForegroundDrawable).addState(new int[] {}, normalDrawable);
        }
        if (mForegroundDrawable != null) mForegroundDrawable.setCallback(this);
        a.recycle();
    }
複製代碼

首先咱們獲取到咱們定義的attrs屬性集,而後獲得drawable,在這裏值得注意的是:

咱們的attr容許傳入的參數除了reference外,還容許color

因此若是傳入的是一個selector,那麼getDrawable將會獲得StateListDrawable

若是傳入的是color值,那麼getDrawable將會獲得ColorDrawable,而ColorDrawable不管是什麼狀態,都只會有一個顏色。

因此若是傳入的是ColorDrawable,咱們就須要手動new出一個StateListDrawable並設置咱們不一樣狀態下的drawable了。

在上述代碼中,若是咱們獲得的是ColorDrawable,咱們就new出StateListDrawable,而後分別對應添加下述狀態和對應的drawable

  • focused狀態 - 則是咱們的前景色drawable
  • pressed狀態 - 同上
  • enable狀態 - 透明色的drawable
  • 無狀態 - 同上

由於咱們也不瞭解究竟是會觸發哪一種狀態,因此就直接扔了大概會觸發的狀態進去,由於在改變的時候,系統會從數組中遍歷直到找出與狀態符合的drawable爲止。

哦,不要忘了setCallback(this),由於View已經實現了該接口,因此咱們把this傳入就行了。

最重要的部分完成後後,接下來就是一些方法的覆寫了。

@Override
    protected void drawableStateChanged() {
        super.drawableStateChanged();

        if (mForegroundDrawable != null && mForegroundDrawable.isStateful()) {
            mForegroundDrawable.setState(getDrawableState());
        }
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (mForegroundDrawable != null) {
            mForegroundDrawable.setBounds(mCachedBounds);
            mForegroundDrawable.draw(canvas);
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        if (mForegroundDrawable != null) mCachedBounds.set(0, 0, w, h);
    }
}
複製代碼

首先咱們覆寫drawableStateChanged,這個方法在view的狀態有發生改變的時候(好比從無焦點->有焦點),就會回調。

在這裏,咱們直接給調用 mForegroundDrawable.setState(getDrawableState());

由於getDrawableState()方法在view裏面已是封裝好的了,它一共執行了兩個動做:

  • 判斷上一次的drawable狀態,若是上一次的狀態不變,就返回上一次的
  • 不然,執行onCreateDrawableState獲取符合的狀態
  • 最終返回與drawablestate長度一致的int數組

在setState以後,咱們直接調用invalidate();要求這個view刷新。

在draw方法裏面,父類的draw方法執行以後,也就是圖片展現以後,咱們調用drawable的draw方法,此時就會將colorDrawable繪製到圖片的上層,表現起來就是有selector的效果了。

最後在xml佈局裏面添加咱們的參數就能夠了:

<razerdp.friendcircle.widget.imageview.ForceClickImageView android:id="@+id/img" android:scaleType="centerCrop" android:layout_width="match_parent" android:layout_height="match_parent" app:foregroundColor="@color/img_foregroundColor" />
複製代碼

其中img_foregroundColor的色值爲: "#85414141"

相關文章
相關標籤/搜索