国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Android > 正文

Android仿京東首頁輪播文字效果

2019-12-12 05:07:00
字體:
供稿:網(wǎng)友

京東客戶端的輪播文字效果:

本次要實現(xiàn)的只是后面滾動的文字(前面的用ImageView或者TextView實現(xiàn)即可),看一下實現(xiàn)的效果


實現(xiàn)思路


上圖只是一個大概的思路,要實現(xiàn)還需要完善更多的細(xì)節(jié),下面會一步步的來實現(xiàn)這個效果:

1.封裝數(shù)據(jù)源:從圖上可以看到,輪播的文字是分為兩個部分的,暫且把它們分別叫做前綴和內(nèi)容,而且實際的使用過程中點擊輪播圖肯定是需要跳轉(zhuǎn)頁面的,而且大部分應(yīng)該是WebView,不妨我們就設(shè)置點擊時候需要獲取的內(nèi)容就是一個鏈接,那么數(shù)據(jù)源的結(jié)構(gòu)就很明了了


創(chuàng)建ADEnity類并完善一些基本的方法,代碼如下

public class ADEnity { private String mFront ; //前面的文字 private String mBack ; //后面的文字 private String mUrl ;//包含的鏈接 public ADEnity(String mFront, String mBack,String mUrl) {  this.mFront = mFront;  this.mBack = mBack;  this.mUrl = mUrl; } public String getmUrl() {  return mUrl; } public void setmUrl(String mUrl) {  this.mUrl = mUrl; } public String getmFront() {  return mFront; } public void setmFront(String mFront) {  this.mFront = mFront; } public String getmBack() {  return mBack; } public void setmBack(String mBack) {  this.mBack = mBack; }}

2.接下來應(yīng)該是定制這個自定義View了,首先理一下思路,看一個構(gòu)造圖


實現(xiàn)這個自定義View的所有參數(shù)都在上表列出了,大部分參數(shù)很容易理解,個別參數(shù)加進去是很有必要的,比如說是否初始化進入文字的縱坐標(biāo),文字是否在移動中這兩個參數(shù),之后的內(nèi)容會詳細(xì)的敘述一下. 在動手繪制之前還得需要知道一點基礎(chǔ)的知識,就是關(guān)于繪制文字的方法,里面有很多細(xì)節(jié)需要處理

方法都比較好理解,繪制指定字符串(可以指定范圍)在坐標(biāo)( x , y )處,但是其中的x,y并不是我們所理解的應(yīng)該是文字左上角的坐標(biāo)點.其中的x坐標(biāo)是根據(jù)Paint的屬性可變換的,默認(rèn)的x是文字的左邊坐標(biāo),如果Paint設(shè)置了paint.setTextAlign(Paint.Align.CENTER);那就是字符的中心位置.Y坐標(biāo)是文字的baseliney坐標(biāo). 關(guān)于繪制文字的baseline:

用圖來說話吧

圖中藍(lán)色的線即為baseline,可以看出他既不是頂部坐標(biāo)也不是底部坐標(biāo),那么當(dāng)我們繪制文字的時候肯定是希望能把文字繪制在正中間.這時候就要引入paint.getTextBound()方法了 getTextBounds(String text, int start, int end, Rect bounds),傳入一個Rect對象,調(diào)用此方法之后則會填充這個rect對象,而填充的內(nèi)容就是所繪制的文字相對于baseline的偏移坐標(biāo),將這個Rect加上baseline的坐標(biāo),繪制后是這樣的:


但其實他的值只是(2,-25,76,3),是相對于baseline的位置,畫個圖會比較好理解

那么要將文字繪制在中間那么實際繪制baseline的坐標(biāo)應(yīng)該是組件的中心加上文字中心即圖中框的中間坐標(biāo)相對于baseline的偏移值">那么要將文字繪制在中間,那么實際繪制baseline的坐標(biāo)應(yīng)該是組件的中心,加上文字中心(即圖中框的中間坐標(biāo))相對于baseline的偏移值


這張圖中應(yīng)該會好理解實際繪制文字的坐標(biāo)與組件中心坐標(biāo)的關(guān)系.關(guān)于偏移值的計算,按常規(guī)的幾何計算方法,應(yīng)該是組件的中心坐標(biāo)+偏移值的絕對值==baseline坐標(biāo)(即實際繪制的坐標(biāo)),但是由于框的坐標(biāo)值都是相對于baseline來計算的,top為負(fù)值,botton為正值,那么這個偏移值就可以直接用(top+bottom)/2來表示,沒看懂的同學(xué)可以畫個草圖,用top=-25,bottom=3來算一下,看是否結(jié)果是一致的.
經(jīng)過上面的理解,那我們來繪制正確繪制文字的方法也就確定了
已獲得組件的高度int mHeight , 文字外框Rect bound的情況下

繪制文字在正中間

 mHeight / 2 - (bound.top + bound.bottom) / 2//在縱坐標(biāo)為mY的地方繪制文字//計算方式//mheight /2 = mY + (bound.top + bound.bottom) / 2 ;

文字滾動到最高點

mY == 0 - bound.bottom//在縱坐標(biāo)為mY的地方繪制,此時文字剛好移動到最高點//計算方式//mY + bound.bottom = 0 ;

文字滾動到最低點,剛好滾出組件

 mY = mHeight - indexBound.top;//在縱坐標(biāo)為mY的地方繪制,此時文字剛好移動到最高點//計算方式//mY + bound.top = mHeight ;

知道了如何正確的繪制文字和邊界情況的坐標(biāo)判斷,下面就到了繪制文字的步驟了
首先初始化數(shù)據(jù),設(shè)置默認(rèn)值

//初始化默認(rèn)值private void init() { mDuration = 500; mInterval = 1000; mIndex = 0; mPaintFront = new Paint(); mPaintFront.setAntiAlias(true); mPaintFront.setDither(true); mPaintFront.setTextSize(30); mPaintBack = new Paint(); mPaintBack.setAntiAlias(true); mPaintBack.setDither(true); mPaintBack.setTextSize(30);}

前面的敘述中我們知道,剛開始進入的時候文字應(yīng)該是位于組件的底部的,但是這個值是需要獲取組件的高度和當(dāng)前顯示文字的情況下來判斷的,所以應(yīng)該放在onDraw內(nèi)來初始化這個值,所以需要前面的是否初始化的屬性,判斷當(dāng)mY==0并且未初始化的時候給mY賦值.
接下來就是onDraw內(nèi)的處理

獲取當(dāng)前的數(shù)據(jù)

//獲取當(dāng)前的數(shù)據(jù)ADEnity model = mTexts.get(mIndex);String font = model.getmFront();String back = model.getmBack();// 繪制前綴的外框Rect indexBound = new Rect();mPaintFront.getTextBounds(font, 0, font.length(), indexBound);//繪制內(nèi)容的外框Rect contentBound = new Rect();mPaintBack.getTextBounds(back, 0, back.length(), contentBound);

對mY進行初始化

if (mY == 0 && hasInit == false) { mY = getMeasuredHeight() - indexBound.top; hasInit = true;}

對邊界情況的處理
/

/移動到最上面if (mY == 0 - indexBound.bottom) { Log.i(TAG, "onDraw: " + getMeasuredHeight()); mY = getMeasuredHeight() - indexBound.top;//返回底部 mIndex++;//換下一組數(shù)據(jù)}//移動到中間if (mY == getMeasuredHeight() / 2 - (indexBound.top + indexBound.bottom) / 2) { isMove = false;//停止移動 Timer timer = new Timer(); timer.schedule(new TimerTask() {  @Override  public void run() {   postInvalidate();//通知重繪   isMove = true;//設(shè)置移動為true  } }, mInterval);//停頓多少毫秒之后再次移動}移動的處理與數(shù)據(jù)源的處理mY -= 1;//每次只移動一個像素,盡量保證平滑顯示//循環(huán)使用數(shù)據(jù)if (mIndex == mTexts.size()) { mIndex = 0;}//如果是處于移動狀態(tài)時的,則延遲繪制//計算公式為一個比例,一個時間間隔移動組件高度,則多少毫秒來移動1像素if (isMove) { postInvalidateDelayed(mDuration / getMeasuredHeight());}至此對邏輯的處理就完成了,接下來要設(shè)置點擊事件public interface onClickLitener { public void onClick(String mUrl);}private onClickLitener onClickLitener;public void setOnClickLitener(TextViewAd.onClickLitener onClickLitener) { this.onClickLitener = onClickLitener;}//重寫onTouchEvent事件,并且要返回true,表明當(dāng)前的點擊事件由這個組件自身來處理@Overridepublic boolean onTouchEvent(MotionEvent event) { int action = event.getAction(); switch (action) {  case MotionEvent.ACTION_DOWN:   //調(diào)用回調(diào),將當(dāng)前數(shù)據(jù)源的鏈接傳出去   if (onClickLitener != null) {    onClickLitener.onClick(mTexts.get(mIndex).getmUrl());   }   break; } return true;}暴露一些其他屬性的設(shè)置方式//設(shè)置數(shù)據(jù)源public void setmTexts(List mTexts) { this.mTexts = mTexts;}//設(shè)置廣告文字的停頓時間public void setmInterval(int mInterval) { this.mInterval = mInterval;}//設(shè)置文字從出現(xiàn)到消失的時長public void setmDuration(int mDuration) { this.mDuration = mDuration;}//設(shè)置前綴的文字顏色public void setFrontColor(int mFrontColor) { mPaintFront.setColor(mFrontColor);}//設(shè)置正文內(nèi)容的顏色public void setBackColor(int mBackColor) { mPaintBack.setColor(mBackColor);}有興趣的同學(xué)可以將這些屬性設(shè)置到attrs.xml文件中然后就可以在布局文件中設(shè)置屬性了,這里就不演示了,因為覺得每次copy這個View還得把xml文件也copy比較麻煩,畢竟as有自動補全,可以很方便的看到暴露在外面的方法.(個人感受而已).貼一下完整的ADTextView的代碼,方便查看package com.qiyuan.jindongshangcheng.view;import android.content.Context;import android.graphics.Canvas;import android.graphics.Paint;import android.graphics.Rect;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.widget.TextView;import com.qiyuan.jindongshangcheng.enity.ADEnity;import java.util.List;import java.util.Timer;import java.util.TimerTask;/** * Created by huanghaojie on 2016/9/30. */public class TextViewAd extends TextView { private int mDuration; //文字從出現(xiàn)到顯示消失的時間 private int mInterval; //文字停留在中間的時長切換的間隔 private List<ADEnity> mTexts; //顯示文字的數(shù)據(jù)源 private int mY = 0; //文字的Y坐標(biāo) private int mIndex = 0; //當(dāng)前的數(shù)據(jù)下標(biāo) private Paint mPaintBack; //繪制內(nèi)容的畫筆 private Paint mPaintFront; //繪制前綴的畫筆 private boolean isMove = true; //文字是否移動 private String TAG = "ADTextView"; private boolean hasInit = false;//是否初始化剛進入時候文字的縱坐標(biāo) public interface onClickLitener {  public void onClick(String mUrl); } private onClickLitener onClickLitener; public void setOnClickLitener(TextViewAd.onClickLitener onClickLitener) {  this.onClickLitener = onClickLitener; } public TextViewAd(Context context) {  this(context, null); } public TextViewAd(Context context, AttributeSet attrs) {  super(context, attrs);  init(); } //重寫onTouchEvent事件,并且要返回true,表明當(dāng)前的點擊事件由這個組件自身來處理 @Override public boolean onTouchEvent(MotionEvent event) {  int action = event.getAction();  switch (action) {   case MotionEvent.ACTION_DOWN:    //調(diào)用回調(diào),將當(dāng)前數(shù)據(jù)源的鏈接傳出去    if (onClickLitener != null) {     onClickLitener.onClick(mTexts.get(mIndex).getmUrl());    }    break;  }  return true; } //設(shè)置數(shù)據(jù)源 public void setmTexts(List mTexts) {  this.mTexts = mTexts; } //設(shè)置廣告文字的停頓時間 public void setmInterval(int mInterval) {  this.mInterval = mInterval; } //設(shè)置文字從出現(xiàn)到消失的時長 public void setmDuration(int mDuration) {  this.mDuration = mDuration; } //設(shè)置前綴的文字顏色 public void setFrontColor(int mFrontColor) {  mPaintFront.setColor(mFrontColor); } //設(shè)置正文內(nèi)容的顏色 public void setBackColor(int mBackColor) {  mPaintBack.setColor(mBackColor); } //初始化默認(rèn)值 private void init() {  mDuration = 500;  mInterval = 1000;  mIndex = 0;  mPaintFront = new Paint();  mPaintFront.setAntiAlias(true);  mPaintFront.setDither(true);  mPaintFront.setTextSize(30);  mPaintBack = new Paint();  mPaintBack.setAntiAlias(true);  mPaintBack.setDither(true);  mPaintBack.setTextSize(30); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) {  super.onSizeChanged(w, h, oldw, oldh);  Log.i(TAG, "onSizeChanged: " + h); } @Override protected void onDraw(Canvas canvas) {  if (mTexts != null) {   Log.i(TAG, "onDraw: " + mY);   //獲取當(dāng)前的數(shù)據(jù)   ADEnity model = mTexts.get(mIndex);   String font = model.getmFront();   String back = model.getmBack();   // 繪制前綴的外框   Rect indexBound = new Rect();   mPaintFront.getTextBounds(font, 0, font.length(), indexBound);   //繪制內(nèi)容的外框   Rect contentBound = new Rect();   mPaintBack.getTextBounds(back, 0, back.length(), contentBound);   //剛開始進入的時候文字應(yīng)該是位于組件的底部的 ,但是這個值是需要獲取組件的高度和當(dāng)前顯示文字的情況下來判斷的,   // 所以應(yīng)該放在onDraw內(nèi)來初始化這個值,所以需要前面的是否初始化的屬性,判斷當(dāng)mY==0并且未初始化的時候給mY賦值.   if (mY == 0 && hasInit == false) {    mY = getMeasuredHeight() - indexBound.top;    hasInit = true;   }   //移動到最上面   if (mY == 0 - indexBound.bottom) {    Log.i(TAG, "onDraw: " + getMeasuredHeight());    mY = getMeasuredHeight() - indexBound.top;//返回底部    mIndex++;//換下一組數(shù)據(jù)   }   canvas.drawText(back, 0, back.length(), (indexBound.right - indexBound.left) + 20, mY, mPaintBack);   canvas.drawText(font, 0, font.length(), 10, mY, mPaintFront);   //移動到中間   if (mY == getMeasuredHeight() / 2 - (indexBound.top + indexBound.bottom) / 2) {    isMove = false;//停止移動    Timer timer = new Timer();    timer.schedule(new TimerTask() {     @Override     public void run() {      postInvalidate();//通知重繪      isMove = true;//設(shè)置移動為true     }    }, mInterval);//停頓多少毫秒之后再次移動   }   //移動的處理與數(shù)據(jù)源的處理   mY -= 1;//每次只移動一個像素,盡量保證平滑顯示   //循環(huán)使用數(shù)據(jù)   if (mIndex == mTexts.size()) {    mIndex = 0;   }   //如果是處于移動狀態(tài)時的,則延遲繪制   //計算公式為一個比例,一個時間間隔移動組件高度,則多少毫秒來移動1像素   if (isMove) {    postInvalidateDelayed(mDuration / getMeasuredHeight());   }  } }}怎么使用呢?1,現(xiàn)在xml文件里引入這個自定義控件<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <com.qiyuan.jindongshangcheng.view.TextViewAd  android:id="@+id/textad"  android:layout_width="match_parent"  android:layout_height="wrap_content" /></LinearLayout>2.在MainActivity中使用/** * Created by huanghaojie on 2016/9/30. */public class MainActivity extends Activity { private TextViewAd textViewAd; private List<ADEnity> mList; @Override protected void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  setContentView(R.layout.activity_main4);  textViewAd = (TextViewAd) findViewById(R.id.textad);  mList = new ArrayList<>();  for (int i = 0; i < 10; i++) {   ADEnity adEnity = new ADEnity("前綴" + i, "后綴" + i, "http://www.baidu.com"+i);   mList.add(adEnity);  }  textViewAd.setmTexts(mList);  textViewAd.setFrontColor(Color.RED);  textViewAd.setBackColor(Color.BLUE);  textViewAd.setmDuration(1000);  textViewAd.setmInterval(1000);  textViewAd.setOnClickLitener(new TextViewAd.onClickLitener() {   @Override   public void onClick(String mUrl) {    Toast.makeText(MainActivity.this,"點擊了"+mUrl,Toast.LENGTH_LONG).show();   }  }); }}

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持武林網(wǎng)。

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 海门市| 类乌齐县| 麻栗坡县| 常宁市| 河北省| 东乡县| 潞城市| 玉龙| 北宁市| 株洲市| 灵寿县| 晋州市| 黎城县| 龙里县| 阳新县| 浦江县| 城步| 安陆市| 奈曼旗| 龙州县| 上高县| 桐梓县| 新昌县| 黎平县| 罗江县| 右玉县| 永济市| 呼和浩特市| 苍溪县| 县级市| 安远县| 文昌市| 凤山县| 防城港市| 洪江市| 高青县| 将乐县| 府谷县| 浙江省| 井研县| 专栏|