一起擼個(gè)朋友圈吧(step5) - 控件篇【點(diǎn)擊展開】

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

上篇鏈接:http://www.lxweimin.com/p/1f85d3978bb5
下篇鏈接:http://www.lxweimin.com/p/26dd3aad965a

終于進(jìn)入自定義控件篇了,不知道有沒有人興奮呢,反正在下很興奮。-V-


本篇將會(huì)實(shí)現(xiàn)一個(gè)比較簡單的控件:點(diǎn)擊展開全文
嗯。。。大概效果圖是這樣的:

效果圖

這個(gè)東東估計(jì)是整一個(gè)工程里最為簡單的一個(gè)控件了,當(dāng)然,網(wǎng)上也有很多例子,實(shí)現(xiàn)的都是類似的,本篇也都是一樣實(shí)現(xiàn)方法。


在開始之前,不妨來想想如何實(shí)現(xiàn)這個(gè)控件,就目前為止,在下想到了兩個(gè)方案:

  • 繼承TextView,通過行數(shù)判斷是否有全文,如果有,則在原文另起一行,通過SpannableStringBuilder拼接原文+\n+包含點(diǎn)擊事件的ClickableSpan,然后動(dòng)態(tài)改變maxLines
  • 通過繼承LinearLayout,該layout包含2個(gè)textview,一個(gè)用來展示,一個(gè)用來點(diǎn)擊,通過行數(shù)判斷是否有全文,如果有,則點(diǎn)擊用的textview設(shè)為visible,否則設(shè)為gone,點(diǎn)擊事件動(dòng)態(tài)改變展示用的textview的maxLines

經(jīng)過測試,第一個(gè)方法實(shí)現(xiàn)較難,主要是因?yàn)榱砥鹨恍械膯栴},因?yàn)樵瓉碚故镜臅r(shí)候已經(jīng)設(shè)置maxLines,如果要另起一行就意味著要增加maxLines,導(dǎo)致原文也展示了出來,然后才是全文,因此不可取,遂采取方案2。


方案想完了,那么我們開工吧

按照我的習(xí)慣,肯定是先定義attrs,因?yàn)閺亩x屬性開始我可以大概設(shè)計(jì)一個(gè)雛形出來。

<declare-styleable name="ClickShowMoreLayout">
      <attr name="show_line" format="integer"/>
      <attr name="click_text" format="string"/>
      <attr name="text_color" format="color"/>
      <attr name="text_size" format="dimension"/>
  </declare-styleable>

我們定義四個(gè)屬性,分別為:最多展示行數(shù),點(diǎn)擊展開的textview文字,展示文字的顏色以及展示文字的大小。

然后就是構(gòu)造器里面一大堆東東,這里就不貼代碼,弄張圖吧

構(gòu)造器初始化

initView方法里面執(zhí)行的是兩個(gè)textview的初始化:

 private void initView(Context context) {
        mTextView=new TextView(context);
        mClickToShow=new TextView(context);

        mTextView.setTextSize(textSize);
        mTextView.setTextColor(textColor);
        mTextView.setMaxLines(showLine);

        mClickToShow.setTextSize(textSize);
        mClickToShow.setTextColor(getResources().getColor(R.color.nick));
        mClickToShow.setText(clickText);

        LinearLayout.LayoutParams params= new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
        params.topMargin= UIHelper.dipToPx(context,10f);
        mClickToShow.setLayoutParams(params);
        mClickToShow.setOnClickListener(this);

        setOrientation(VERTICAL);
        addView(mTextView);
        addView(mClickToShow);
    }

值得注意的是,我們的layoutparams不可以通過getLayoutParams拿到哦,那是個(gè)空的。

重頭戲是設(shè)置文字的方法和onClick方法:

 public void setText(String str){
        mTextView.setText(str);
        mTextView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
            @Override
            public boolean onPreDraw() {
                if (!hasGetLineCount) {
                    hasMore = mTextView.getLineCount() > showLine;
                    hasGetLineCount=true;
                }
                mClickToShow.setVisibility(hasMore?VISIBLE:GONE);
                mTextView.getViewTreeObserver().removeOnPreDrawListener(this);
                return true;
            }
        });
    }

@Override
public void onClick(View v) {
        if (((TextView)v).getText().toString().equals(clickText)){
            mTextView.setMaxLines(Integer.MAX_VALUE);
            mClickToShow.setText("收起");
        }else {
            mTextView.setMaxLines(showLine);
            mClickToShow.setText(clickText);
        }
    }   
    

要判斷是否有更多內(nèi)容,我們只能獲取TextView繪制出來時(shí)文字的總行數(shù),那么文字都還沒繪制上去,我們應(yīng)該怎么拿到這個(gè)總行數(shù)呢,我們當(dāng)然不可以在onDraw里面拿,于是我們就在onPreDraw里面拿,關(guān)于這里,待會(huì)說說。
拿到總行數(shù)后跟我們的最大顯示行數(shù)比較一下,然后得到是否有更多進(jìn)而設(shè)置點(diǎn)擊textview的可見性就完成了。

本篇完成,下篇將會(huì)實(shí)現(xiàn)點(diǎn)贊展示的控件。


以下涉及官方源碼,稍微枯燥,可以跳過。

關(guān)于onPreDraw(),文檔是這么描述的:

Callback method to be invoked when the view tree is about to be drawn. At this point, all views in the tree have been measured and given a frame. Clients can use this to adjust their scroll bounds or even to request a new layout before drawing occurs.

(大概意思是【渣翻】:view樹在onDraw之前會(huì)回調(diào)這個(gè)方法,此時(shí)view已經(jīng)進(jìn)行過measure【也就是可以拿到寬高】,在draw之前,我們可以進(jìn)行滑動(dòng)界限的調(diào)整【應(yīng)該是類似與ViewDragHelper那個(gè)邊界限定吧】甚至是重新部署)

而TextView關(guān)于文字的部署,其實(shí)是與兩個(gè)layout有關(guān),一個(gè)是StaticLayout,一個(gè)是DynamicLayout這兩者的區(qū)別簡單來說就是setText時(shí)用的是普通的字符串還是包含有span的字符集合(另外還有BoringLayout 用于單行字符串的)。【詳情請(qǐng)谷歌TextView渲染機(jī)制】

而既然textview實(shí)現(xiàn)了ViewTreeObserver.OnPreDrawListener這個(gè)接口,那么textview必定實(shí)現(xiàn)了這個(gè)方法回調(diào),而且必定是有得到StaticLayout或者DynamicLayout,否則我們得到的linecount只能為0.

于是我們打算找找是否實(shí)現(xiàn)了StaticLayout

我們可以查看TextView源碼:

onPreDraw

在assumeLayout我們又可以找到這個(gè)方法

makeNewLayout(width, physicalWidth, UNKNOWN_BORING, UNKNOWN_BORING,physicalWidth, false);

其他參數(shù)先不管,我們先找到目標(biāo)
在這個(gè)方法里面我們繼續(xù)可以找到這個(gè)方法

makeNewLayout

在這個(gè)方法里面,我們終于找到了我們需要的東西了:

makeSingleLayout
makeSingleLayout

可以看到,如果有span使用的是dynamiclayout,如果是singleline,則用boringlayout,如果都沒有(即result==null),則用的是staticlayout

我們不妨繼續(xù)看看build里面的方法:

 public StaticLayout build() {
            StaticLayout result = new StaticLayout(this);
            Builder.recycle(this);
            return result;
        }

而StaticLayout最終調(diào)用的是Layout的方法,然而Layout的getLineCount是抽象方法,那么只能是StaticLayout實(shí)現(xiàn)這個(gè)方法了,經(jīng)過一直查找,最終發(fā)現(xiàn)在out這個(gè)私有方法里面有關(guān)于mLineCount的計(jì)數(shù)

out

其實(shí)到這里,我們可以大膽猜測控件或者其他什么元素的擺放是一行一行來放的,如果超過了,則另起一行直到放完為止。當(dāng)然,這僅僅是我的猜測,并沒有去查證。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,702評(píng)論 6 534
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,615評(píng)論 3 419
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,606評(píng)論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,044評(píng)論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,826評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,227評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,307評(píng)論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,447評(píng)論 0 289
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,992評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,807評(píng)論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,001評(píng)論 1 370
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,550評(píng)論 5 361
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,243評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,667評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,930評(píng)論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,709評(píng)論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,996評(píng)論 2 374

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