Android中無(wú)限滾動(dòng)的Image

前言

這是新開(kāi)的博客第一篇文章攘乒。這一篇針對(duì)的是自定義控件盼砍。在github上有一個(gè)自定義控件的效果如下:

效果

這個(gè)水平方向上無(wú)限滾動(dòng)的控件篮灼,可以用來(lái)制作自定義進(jìn)度條生年,或者一些tab效果。
具體使用方法請(qǐng)移步github晋辆。
它實(shí)現(xiàn)該效果只有50行代碼不到渠脉,所以寫(xiě)這篇博客來(lái)記錄該控件的實(shí)現(xiàn)過(guò)程。

1. 分析需求

  • 設(shè)置進(jìn)去的圖片可以水平滾動(dòng)瓶佳;
  • 滾動(dòng)展示的圖片可以自行設(shè)置芋膘,滾動(dòng)的速度也可以自行設(shè)置,霸饲;
  • 速度為正为朋,向后滾動(dòng)(從右向左滾動(dòng));為負(fù)時(shí)厚脉,向前滾動(dòng)(從左向右滾動(dòng))习寸;

2. xml資源相關(guān)

能夠自行設(shè)置,那么需要去設(shè)置自定義屬性來(lái)控制速度及滾動(dòng)展示的圖片傻工。
在res/values下創(chuàng)建attrs.xml霞溪,并采用以下方式定義自定義屬性:

 <resource>
<declare-styleable name="ScrollingView">
    <attr name="speed" format="dimension" />
    <attr name="src" format="reference" />
</declare-styleable>
</resource>

speed為速度孵滞,src為滾動(dòng)展示的圖片資源。
在layout布局文件中使用自定義屬性鸯匹,首先在根布局view中設(shè)置命名空間:

    xmlns:app="http://schemas.android.com/apk/res-auto"

然后在自定義控件中設(shè)置自定義屬性

layout布局中使用自定義屬性

3. java代碼

3.1 初始化

創(chuàng)建一個(gè)View類(lèi)坊饶,并在構(gòu)造方法中獲得自定義屬性speed和圖片。凡是在xml中定義的控件殴蓬,會(huì)調(diào)用以下形式的構(gòu)造方法:

public ScrollingView(Context context, AttributeSet attrs) {
    super(context, attrs);
     try{
        speed = typedArray.getDimensionPixelSize(R.styleable.ScrollingView_speed, 1);
        bitmap = BitmapFactory.decodeResource(getResources(), typedArray.getResourceId(R.styleable.ScrollingView_src, 0));
    }finally {
        typedArray.recycle();
    }
}

3.2 onMeasure測(cè)量

需要設(shè)置控件的寬高匿级,不然會(huì)出現(xiàn)高度顯示不正常。這里采用的方式為:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), bitmap.getHeight());
}

3.3 onDraw繪制

關(guān)鍵點(diǎn)來(lái)了染厅,該控件最大的難點(diǎn)就在繪制痘绎,我將繪制邏輯分為三個(gè)要點(diǎn):

3.3.1 要點(diǎn)一

如果當(dāng)前的控件寬度大于要滾動(dòng)的圖片寬度,那么會(huì)出現(xiàn)空白肖粮,應(yīng)該需要重復(fù)繪制多個(gè)圖片來(lái)填充孤页。


出現(xiàn)空白
  • 解決方案:用一個(gè)變量left記錄當(dāng)前canvas繪制bitmap時(shí)的左方基坐標(biāo),如果left小于控件的寬度尿赚,就繼續(xù)繪制散庶,每繪制一次圖片蕉堰,就讓left的值遞增凌净,每次遞增的值為bitmap的寬度,這種方式可以實(shí)現(xiàn)從左往右的繪制圖片出來(lái)填充滿(mǎn)控件屋讶。
填補(bǔ)空白

具體代碼如下:

protected void onDraw(Canvas canvas) {
    //獲取要滾動(dòng)的圖片的寬度
    float layerWidth = bitmap.getWidth();
    //使用一個(gè)變量來(lái)作為繪制bitmap時(shí)的左邊基坐標(biāo)
    float left = 0;
    //如果left不大于控件的寬度冰寻,則循環(huán)繪制
    while (left < getMeasuredWidth()) {
        canvas.drawBitmap(bitmap, left, 0, null);
        left += layerWidth;
    }
}

3.3.2 要點(diǎn)二

根據(jù)speed屬性,來(lái)設(shè)置圖片往后退的速度皿渗。

  • 解決方案:通過(guò)不斷修改canvas.drawBitmap()中的left斩芭,根據(jù)speed,不斷的讓left坐標(biāo)變小乐疆,并調(diào)用重繪方法划乖,從而使得圖片不斷后退。關(guān)于left變小的偏移量挤土,使用全局變量offset進(jìn)行記錄琴庵。
向后滾動(dòng)

具體代碼如下:

private float offset;
@Override
protected void onDraw(Canvas canvas) {
    //獲取要滾動(dòng)的圖片的寬度
    float layerWidth = bitmap.getWidth();
    //使用一個(gè)變量來(lái)作為繪制bitmap的左邊基坐標(biāo)
    float left = offset;
    //如果left不大于控件的寬度,則循環(huán)繪制
    while (left < getMeasuredWidth()) {
        canvas.drawBitmap(bitmap, left, 0, null);
        left += layerWidth;
    }
    //全局變量offser用來(lái)記錄left的偏移量
    offset = offset-speed;
    postInvalidate();
}

這樣寫(xiě)導(dǎo)致left的偏移量越來(lái)越小仰美,代碼會(huì)在手機(jī)屏幕左方看不見(jiàn)的地方瘋狂繪制迷殿,這顯然不太效率。這里多加一個(gè)判斷咖杂,如果offset超過(guò)一定的界限庆寺,就重置。

protected void onDraw(Canvas canvas) {

    //獲取要滾動(dòng)的圖片的寬度
    float layerWidth = bitmap.getWidth();
    if (offset < -layerWidth) {
        offset += (floor(abs(offset) / layerWidth) * layerWidth);
        //offset = 0;
    }
    ...
}

3.3.3 要點(diǎn)三

如果速度設(shè)置為負(fù)數(shù)诉字,圖片應(yīng)該不再后退懦尝,而是不斷前進(jìn)知纷。

  • 解決方案:并讓左方基坐標(biāo)不斷變大,并改變繪制的方向陵霉,改為從右往左的方向繪制圖片屈扎,就可以讓圖片變成向前滾了。
    • 如果需要從右向左撩匕,那么開(kāi)始繪制第一個(gè)圖片時(shí)鹰晨,左方的基坐標(biāo)不再是從0開(kāi)始,而是:控件的寬度-圖片bitmap的寬度止毕。
    • 在當(dāng)前的情況下模蜡,為了left變量總體趨勢(shì)不斷變小,同時(shí)能夠不斷的讓左方基坐標(biāo)變大扁凛,所以在循環(huán)當(dāng)中忍疾,左方基坐標(biāo)改為getMeasureWidth()-bitmap.getWidth()-left。
    • left變量總體趨勢(shì)不斷變小谨朝,offset也應(yīng)該不斷變小卤妒,而當(dāng)前speed為負(fù),所以在對(duì)offset偏移量的減去speed操作時(shí)字币,對(duì)speed采用絕對(duì)值abs则披。
向前滾動(dòng)
protected void onDraw(Canvas canvas) {
    //獲取要滾動(dòng)的圖片的寬度
    float layerWidth = bitmap.getWidth();
    ...
    //如果left不大于控件的寬度,則循環(huán)繪制
    while (left < getMeasuredWidth()) {
        canvas.drawBitmap(bitmap, getBitmapLeft(layerWidth, left), 0, null);
        left += layerWidth;
    }
    //全局變量offser用來(lái)記錄left的偏移量
    if(isStarted){
        offset = offset-abs(speed);
        postInvalidate();
    }
}

/**
 * @param layerWidth bitmap圖片的寬度
 * @return
 */
private float getBitmapLeft(float layerWidth, float left) {
    if (speed < 0) {
        return getMeasuredWidth() - layerWidth - left;
    } else {
        return left;
    }
}

3.4 功能完善

為了能夠讓開(kāi)發(fā)者自由控制圖片滾動(dòng)洗出,項(xiàng)目中還加了一個(gè)boolean值用來(lái)控制士复。并提供對(duì)應(yīng)的公有方法。


停止?jié)L動(dòng)

具體代碼如下:

private boolean isStarted = false;

public ScrollingView(Context context, AttributeSet attrs) {
    ...
    start();
}

/**Start the animation*/
public void start() {
    if (!isStarted) {
        isStarted = true;
        postInvalidate();
    }
}

protected void onDraw(Canvas canvas) {
    ...
    //全局變量offser用來(lái)記錄left的偏移量
    if(isStarted){
        offset = offset-speed;
        postInvalidate();
    }
}

/**Stop the animation*/
public void stop() {
    if (isStarted) {
        isStarted = false;
        invalidate();
    }
}
public boolean isStarted(){
    return isStarted;
}

結(jié)束語(yǔ)

有興趣的小伙伴可以參考這個(gè)思路翩活,通過(guò)修改drawBitmap方法中top基坐標(biāo)阱洪,實(shí)現(xiàn)一下Image在豎直方向上的無(wú)限滾動(dòng)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末菠镇,一起剝皮案震驚了整個(gè)濱河市冗荸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌利耍,老刑警劉巖蚌本,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異堂竟,居然都是意外死亡魂毁,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)出嘹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)席楚,“玉大人,你說(shuō)我怎么就攤上這事税稼》持龋” “怎么了垮斯?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)只祠。 經(jīng)常有香客問(wèn)我兜蠕,道長(zhǎng),這世上最難降的妖魔是什么抛寝? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任熊杨,我火速辦了婚禮,結(jié)果婚禮上盗舰,老公的妹妹穿的比我還像新娘晶府。我一直安慰自己,他們只是感情好钻趋,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布川陆。 她就那樣靜靜地躺著,像睡著了一般蛮位。 火紅的嫁衣襯著肌膚如雪较沪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,146評(píng)論 1 297
  • 那天失仁,我揣著相機(jī)與錄音尸曼,去河邊找鬼。 笑死陶因,一個(gè)胖子當(dāng)著我的面吹牛骡苞,可吹牛的內(nèi)容都是我干的垂蜗。 我是一名探鬼主播楷扬,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼贴见!你這毒婦竟也來(lái)了烘苹?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤片部,失蹤者是張志新(化名)和其女友劉穎镣衡,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體档悠,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡廊鸥,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了辖所。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惰说。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖缘回,靈堂內(nèi)的尸體忽然破棺而出吆视,到底是詐尸還是另有隱情典挑,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布啦吧,位于F島的核電站您觉,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏授滓。R本人自食惡果不足惜琳水,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望般堆。 院中可真熱鬧炫刷,春花似錦、人聲如沸郁妈。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)噩咪。三九已至顾彰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間胃碾,已是汗流浹背涨享。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留仆百,地道東北人厕隧。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像俄周,于是被迫代替她去往敵國(guó)和親吁讨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,072評(píng)論 25 707
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)峦朗、插件建丧、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,093評(píng)論 4 62
  • 通常我們用Push在進(jìn)行控制器跳轉(zhuǎn)的時(shí)候,大家的一般做法是 這里其實(shí)是有一個(gè)陷阱波势,通常我們會(huì)誤認(rèn)為控制器初始化in...
    ___1o_8o閱讀 117評(píng)論 0 1
  • 凌云和她的媽媽在縣城醫(yī)院翎朱,林正決定我們先去凌云老家的市區(qū),找好醫(yī)院和主刀醫(yī)生尺铣,然后再去縣城把凌云的媽媽接過(guò)來(lái)拴曲。林正...
    作家二美閱讀 403評(píng)論 0 1
  • 愿世界化為滄海 愿你我歸于初見(jiàn)。
    甜公子602閱讀 155評(píng)論 0 0