一起擼個朋友圈吧 - 點贊列表背景問題修復

項目地址:https://github.com/razerdp/FriendCircle
一起擼個朋友圈吧這是本文所處文集位岔,所有更新都會在這個文集里面哦座云,歡迎關(guān)注


首先非常感謝@奉孝安在在文章中的評論,他發(fā)現(xiàn)在《一起擼個朋友圈吧(step5) - 控件篇【點贊列表】》這篇文章中蹬音,我寫的點贊列表并沒有實現(xiàn)selector效果赚抡。

在在幾分鐘前板辽,我留下了一個評論剿干,說是可以在LinkMovementMethod里面處理担神,然后本來想去打Dota2來著,結(jié)果這個問題一直壓在心里不舒服演痒,所以就花了一點事件來解決這個bug亲轨。

首先上preview:

preview

在此之前,點擊span是沒有任何背景色改變的

ps:本次上傳到github只是上傳到了main-dev分支嫡霞,并沒有合并到master分支


關(guān)于Span

事實上瓶埋,在那篇文章的后面我有稍微講解過希柿,但我并沒有談到關(guān)于span的問題诊沪。

事實上养筒,關(guān)于textview,我們的text是如何分辨出那么多的span的端姚?
這里我們拿到BackgroundColorSpan來做例子:

public class BackgroundColorSpan extends CharacterStyle
        implements UpdateAppearance, ParcelableSpan {

    private final int mColor;

    public BackgroundColorSpan(int color) {
        mColor = color;
    }

    public BackgroundColorSpan(Parcel src) {
        mColor = src.readInt();
    }
    
    public int getSpanTypeId() {
        return getSpanTypeIdInternal();
    }

    /** @hide */
    public int getSpanTypeIdInternal() {
        return TextUtils.BACKGROUND_COLOR_SPAN;
    }
    
    public int describeContents() {
        return 0;
    }

    public void writeToParcel(Parcel dest, int flags) {
        writeToParcelInternal(dest, flags);
    }

    /** @hide */
    public void writeToParcelInternal(Parcel dest, int flags) {
        dest.writeInt(mColor);
    }

    public int getBackgroundColor() {
        return mColor;
    }

    @Override
    public void updateDrawState(TextPaint ds) {
        ds.bgColor = mColor;
    }
}

代碼很少晕粪,我們可以看到這個帶背景的span實現(xiàn)了Parcelable這個序列化字眼的接口,看看接口的介紹:

/**
 * A special kind of Parcelable for objects that will serve as text spans.
 * This can only be used by code in the framework; it is not intended for
 * applications to implement their own Parcelable spans.
 */
public interface ParcelableSpan extends Parcelable

這是一個特殊的序列化對象渐裸,它用來保存span巫湘。這個借口僅供framework用,不應(yīng)該應(yīng)用層編寫的時候使用昏鹃。

它實現(xiàn)了三個方法:

  /**
     * Return a special type identifier for this span class.
     */
    int getSpanTypeId();

    /**
     * Internal implementation of {@link #getSpanTypeId()} that is not meant to
     * be overridden outside of the framework.
     *
     * @hide
     */
    int getSpanTypeIdInternal();

    /**
     * Internal implementation of {@link Parcelable#writeToParcel(Parcel, int)}
     * that is not meant to be overridden outside of the framework.
     *
     * @hide
     */
    void writeToParcelInternal(Parcel dest, int flags);

通過著三個方法尚氛,我們很容易猜測到,span是通過getSpanTypeId得到span對應(yīng)的class洞渤,也許是系統(tǒng)有一個工廠類來對應(yīng)創(chuàng)建這些span阅嘶。一般這個方法會調(diào)用getSpanTypeIdInternal(),其實區(qū)別不大载迄,只是因為有了一個@hide的java doc-V-

回到我們的BackgroundColorSpan讯柔,在getSpanTypeIdInternal中,返回的是

return TextUtils.BACKGROUND_COLOR_SPAN;

而這個我們是不可見的护昧,然而我們?nèi)extUtils看一看魂迄,就會發(fā)現(xiàn)很多這種類型:

TextUtils

只不過都不讓我們看而已。

最終這些配置會被這個方法調(diào)用:

如無意外惋耙,最終都是底層的native調(diào)用捣炬。我們暫時先不追溯下去。

由此我們可以看到一個span是怎樣把信息傳遞的绽榛。

比如BackgroundColorSpan遥金,將背景顏色序列化,然后c層調(diào)用的時候反序列并傳到底層進行創(chuàng)建蒜田,這樣一些span的信息就傳遞了出去稿械。


updateDrawState

當然,我們并不需要涉及到那么深冲粤,在BackgroundColorSpan我們可以看到一個地方:

@Override
public void updateDrawState(TextPaint ds) {    
  ds.bgColor = mColor;
}

而mColor正是初始化時傳遞進去的背景美莫,那么updateDrawState這個方法到底是何方神圣呢,在as我們查看到這個方法幾乎是所有span都有實現(xiàn)梯捕,畢竟幾乎都繼承CharacterStyle

updateDrawState

(同時我也驚訝于厢呵,,傀顾,原來有這么多的span)

然而襟铭,很高興的在開發(fā)者文檔里面我們也找不到它的描述。

但我們通過名字可以猜測,是不是在draw調(diào)用時會回調(diào)寒砖,而且傳入的是一個TextPaint赐劣,如無意外,這正是BackgroundColorSpan實現(xiàn)帶背景的span的做法哩都,使用TextPaint為span設(shè)置背景色魁兼。


改進點贊控件

前文我說過,span的點擊事件都是在LinkMovementMethod處理的漠嵌。而span實現(xiàn)的updateDrawState就是關(guān)鍵性方法咐汞,所以我們可以繼承LinkMovementMethod實現(xiàn)更改背景色:

  • 點擊時(ACTION_DOWN),調(diào)用我們的span里面更改顏色的方法
  • 松開手(NOT ACTION_DOWN)儒鹿,則設(shè)置為原來的顏色(本例是透明色)

完成這兩點就可以了化撕。

LinkMovementMethod修改:

首先我們繼承LinkMovementMethod,取名PraiseMovementMethod

public class PraiseMovementMethod extends LinkMovementMethod {
    private static PraiseMovementMethod sInstance;
    private int color;
    private int defaultBgColor;

    private PraiseMovementMethod(int color, int defaultBgColor) {
        this.color = color;
        this.defaultBgColor = defaultBgColor;
    }

    @Override
    public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {

    }

    public static PraiseMovementMethod getInstance(int color, int defaultBackgroungColor) {
        if (sInstance == null) {
            sInstance = new PraiseMovementMethod(color, defaultBackgroungColor);
        }
        return sInstance;
    }
}

基本跟父類保持一致约炎,但我們只實現(xiàn)三個方法侯谁,一個私有構(gòu)造器,一個getInstance章钾,一個就是touch事件墙贱。

我們傳入兩個值,一個是點擊時候的顏色贱傀,一個是沒有點擊的時候的顏色惨撇。

然后我們在onTouchEvent進行修改,不過請放心府寒,我們并非需要重新梳理一遍事件分發(fā)/事件處理魁衙,因為這些父類已經(jīng)做好了,我們要做的僅僅是copy code from the super class:

    @Override
    public boolean onTouchEvent(TextView widget, Spannable buffer,
                                MotionEvent event) {
        int action = event.getAction();

        if (action == MotionEvent.ACTION_UP ||
            action == MotionEvent.ACTION_DOWN) {
            int x = (int) event.getX();
            int y = (int) event.getY();

            x -= widget.getTotalPaddingLeft();
            y -= widget.getTotalPaddingTop();

            x += widget.getScrollX();
            y += widget.getScrollY();

            Layout layout = widget.getLayout();
            int line = layout.getLineForVertical(y);
            int off = layout.getOffsetForHorizontal(line, x);

            ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);

            if (link.length != 0) {
                if (action == MotionEvent.ACTION_UP) {
                    link[0].onClick(widget);
                } else if (action == MotionEvent.ACTION_DOWN) {
                    Selection.setSelection(buffer,
                                           buffer.getSpanStart(link[0]),
                                           buffer.getSpanEnd(link[0]));
                }

                return true;
            } else {
                Selection.removeSelection(buffer);
            }
        }

        return super.onTouchEvent(widget, buffer, event);
    }

我們詳細的看看這段代碼:

 ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);

            if (link.length != 0) {
                if (action == MotionEvent.ACTION_UP) {
                    link[0].onClick(widget);
                } else if (action == MotionEvent.ACTION_DOWN) {
                    Selection.setSelection(buffer,
                                           buffer.getSpanStart(link[0]),
                                           buffer.getSpanEnd(link[0]));
                }

                return true;
            } else {
                Selection.removeSelection(buffer);
            }
        }

這里處理的是在點擊獲得當前點擊位置后株搔,先拿到ClickableSpan數(shù)組剖淀,如果數(shù)組長度不為0,證明當前點擊的是span纤房,在up時實現(xiàn)其onClick方法纵隔。

很明顯,我們需要在這里做手腳炮姨。不過ClickableSpan并不能滿足我們的需求捌刮,所以我們將其改動一下:

PraiseClick[] link = buffer.getSpans(off, off, PraiseClick.class);

換成得到我們的PraiseClick

接下來回到我們的PraiseClick

PraiseClick修改:

首先添加一個變量,作為背景顏色舒岸,由于我們朋友圈默認是透明的背景色绅作,所以就在定義變量時就賦予了初值:

private int clickBgColor= Color.TRANSPARENT;

然后在updateDrawState進行paint的背景色賦值:

 @Override
    public void updateDrawState(TextPaint ds) {
        super.updateDrawState(ds);
...原代碼不變
        ds.bgColor=clickBgColor;
...原代碼不變
    }

最后暴露一個公共setter方法給外部調(diào)用:

  public void setClickBackgroundColor(int color){
        this.clickBgColor=color;
    }

就是這么粗暴簡單。

**回到我們的PraiseMovementMethod **
繼續(xù)修改touch:

      if (link.length != 0) {
                if (action == MotionEvent.ACTION_UP) {
                    link[0].setClickBackgroundColor(defaultBgColor);
                    link[0].onClick(widget);
                }
                else if (action == MotionEvent.ACTION_DOWN) {
                    Selection.setSelection(buffer, buffer.getSpanStart(link[0]), buffer.getSpanEnd(link[0]));
                    link[0].setClickBackgroundColor(color);
                }
                else {
                    link[0].setClickBackgroundColor(defaultBgColor);
                }

在action_down里面蛾派,設(shè)置點擊顏色俄认,其余情況都設(shè)置為初始值(本例為透明)

就是這樣个少,我們就完成了本次的修復。

最后如果您在使用過程中發(fā)現(xiàn)什么bug或者我沒留意到的眯杏,也可以在評論區(qū)留下您的腳印哦夜焦,如果我有這個能力,我會盡快解決的-V-

最后最后再次感謝@奉孝安在的提議役拴。


03-27更新

在評論區(qū)@奉孝安在貼上了他的解決方案糊探,當然钾埂,方法是不變的河闰。不過他是把本該LinkMovementMethod的處理直接封裝到了clickablespan里面,然后使用內(nèi)部類解決Touch事件褥紫。

預覽圖:

preview

具體看代碼:
https://github.com/LiesSu/AndroidExtenders/blob/master/app/src/main/java/com/liessu/extender/span/ClickableSpanEx.java

當然姜性,基于他的代碼我稍微做了一點點改動。

這里說一下為什么我會采取這個方案髓考。

首先我們不用針對寫LinkMovementMethod外部念,事實上我覺得既然自己內(nèi)部類接管了Touch事件,我們可以說不需要LinkMovementMethod氨菇。

除了上面這點儡炼,最重要的是事件攔截。

我們的評論控件承擔著三個事件:

  • 內(nèi)部用戶的span點擊事件
  • 外部TextView點擊彈出輸入法
  • 外部TextView長按彈出選擇框(復制/刪除)

然而如果我們點擊span勢必會點到整個TextView查蓉,這意味著如果我們點了span就會觸發(fā)鍵盤彈出事件乌询。

而如果按照原來的做法,我們需要在LinkMovementMethod里面用個標志位記錄豌研,然后在外部處理的時候拿到這個標志位判斷是點的span還是整個textview妹田,但現(xiàn)在因為@奉孝安在直接使用內(nèi)部類處理,所以我們可以直接進行攔截鹃共。

這就是我選擇這個方案的原因鬼佣。

最后感謝@奉孝安在的解決方案-V-

這才叫“一起擼個朋友圈”嘛,最重要的不是弄出一個什么app霜浴,而是一起這兩個字晶衷。(一直玩著單機的我忽然遇到一位留下評論的人,好激動)

如果您有好的建議阴孟,提議房铭,希望能在評論區(qū)看到您的腳印或者GitHub提交PR。在下萬分感謝-V-

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末温眉,一起剝皮案震驚了整個濱河市缸匪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌类溢,老刑警劉巖凌蔬,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件露懒,死亡現(xiàn)場離奇詭異,居然都是意外死亡砂心,警方通過查閱死者的電腦和手機懈词,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來辩诞,“玉大人坎弯,你說我怎么就攤上這事∫朐荩” “怎么了抠忘?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長外永。 經(jīng)常有香客問我崎脉,道長,這世上最難降的妖魔是什么伯顶? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任囚灼,我火速辦了婚禮,結(jié)果婚禮上祭衩,老公的妹妹穿的比我還像新娘灶体。我一直安慰自己,他們只是感情好掐暮,可當我...
    茶點故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布蝎抽。 她就那樣靜靜地躺著,像睡著了一般劫乱。 火紅的嫁衣襯著肌膚如雪织中。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天衷戈,我揣著相機與錄音狭吼,去河邊找鬼。 笑死殖妇,一個胖子當著我的面吹牛刁笙,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播谦趣,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼疲吸,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了前鹅?” 一聲冷哼從身側(cè)響起摘悴,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎舰绘,沒想到半個月后蹂喻,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體葱椭,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年口四,在試婚紗的時候發(fā)現(xiàn)自己被綠了孵运。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡蔓彩,死狀恐怖治笨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情赤嚼,我是刑警寧澤旷赖,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站探膊,受9級特大地震影響杠愧,放射性物質(zhì)發(fā)生泄漏待榔。R本人自食惡果不足惜逞壁,卻給世界環(huán)境...
    茶點故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望锐锣。 院中可真熱鬧腌闯,春花似錦、人聲如沸雕憔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽斤彼。三九已至分瘦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間琉苇,已是汗流浹背嘲玫。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留并扇,地道東北人去团。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像穷蛹,于是被迫代替她去往敵國和親土陪。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,507評論 2 359

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

  • 項目地址:https://github.com/razerdp/FriendCircle一起擼個朋友圈吧這是本文所...
    Razerdp閱讀 1,972評論 0 8
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理肴熏,服務(wù)發(fā)現(xiàn)鬼雀,斷路器,智...
    卡卡羅2017閱讀 134,697評論 18 139
  • 前言 工作找完了蛙吏,已經(jīng)干了兩個星期源哩。雖然經(jīng)常加班蹋肮,不過相比之前的工作,現(xiàn)在過得更加充實璧疗、更有意義∨鞅纾現(xiàn)在有點空閑時間...
    帶心情去旅行閱讀 72,697評論 42 237
  • 家里好像催婚了却音,不過90后好像也不是很年輕了改抡,更何況我是1990年的。 每到年關(guān)時節(jié)系瓢,這是很多人歡樂的日子也是很多...
    閑少閱讀 399評論 9 1
  • 本文寫于 20190506阿纤,屬于“半吊子”系列連載文章。 1 領(lǐng)路人貓老師 華安有兩個微信號夷陋。也有兩個朋友圈欠拾。 ...
    靜聽旁白閱讀 512評論 0 0