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

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

Android實(shí)現(xiàn)圖片滾動(dòng)和頁(yè)簽控件功能的實(shí)現(xiàn)代碼

2019-10-22 18:11:36
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

首先題外話,今天早上起床的時(shí)候,手滑一下把我的手機(jī)甩了出去,結(jié)果陪伴我兩年半的摩托羅拉里程碑一代就這么安息了,于是我今天決定怒更一記,紀(jì)念我死去的愛(ài)機(jī)。

如果你是網(wǎng)購(gòu)達(dá)人,你的手機(jī)上一定少不了淘寶客戶端。關(guān)注特效的人一定都會(huì)發(fā)現(xiàn),淘寶不管是網(wǎng)站還是手機(jī)客戶端,主頁(yè)上都會(huì)有一個(gè)圖片滾動(dòng)播放器,上面展示一些它推薦的商品。這個(gè)幾乎可以用淘寶來(lái)冠名的功能,看起來(lái)還是挺炫的,我們今天就來(lái)實(shí)現(xiàn)一下。

實(shí)現(xiàn)原理其實(shí)還是之前那篇文章Android仿人人客戶端滑動(dòng)菜單的側(cè)滑菜單效果,史上最簡(jiǎn)單的側(cè)滑實(shí)現(xiàn)  ,算是以那個(gè)原理為基礎(chǔ)的另外一個(gè)變種。正所謂一通百通,真正掌握一種方法之后,就可以使用這個(gè)方法變換出各種不通的效果。

今天仍然還是實(shí)現(xiàn)一個(gè)自定義控件,然后我們?cè)谌我釧ctivity的布局文件中引用一下,即可實(shí)現(xiàn)圖片滾動(dòng)器的效果。

在Eclipse中新建一個(gè)Android項(xiàng)目,項(xiàng)目名就叫做SlidingViewSwitcher。

新建一個(gè)類(lèi),名叫SlidingSwitcherView,這個(gè)類(lèi)是繼承自RelativeLayout的,并且實(shí)現(xiàn)了OnTouchListener接口,具體代碼如下:

public class SlidingSwitcherView extends RelativeLayout implements OnTouchListener {  /**  * 讓菜單滾動(dòng),手指滑動(dòng)需要達(dá)到的速度。  */  public static final int SNAP_VELOCITY = 200;  /**  * SlidingSwitcherView的寬度。  */  private int switcherViewWidth;  /**  * 當(dāng)前顯示的元素的下標(biāo)。  */  private int currentItemIndex;  /**  * 菜單中包含的元素總數(shù)。  */  private int itemsCount;  /**  * 各個(gè)元素的偏移邊界值。  */  private int[] borders;  /**  * 最多可以滑動(dòng)到的左邊緣。值由菜單中包含的元素總數(shù)來(lái)定,marginLeft到達(dá)此值之后,不能再減少。  *  */  private int leftEdge = 0;  /**  * 最多可以滑動(dòng)到的右邊緣。值恒為0,marginLeft到達(dá)此值之后,不能再增加。  */  private int rightEdge = 0;  /**  * 記錄手指按下時(shí)的橫坐標(biāo)。  */  private float xDown;  /**  * 記錄手指移動(dòng)時(shí)的橫坐標(biāo)。  */  private float xMove;  /**  * 記錄手機(jī)抬起時(shí)的橫坐標(biāo)。  */  private float xUp;  /**  * 菜單布局。  */  private LinearLayout itemsLayout;  /**  * 標(biāo)簽布局。  */  private LinearLayout dotsLayout;  /**  * 菜單中的第一個(gè)元素。  */  private View firstItem;  /**  * 菜單中第一個(gè)元素的布局,用于改變leftMargin的值,來(lái)決定當(dāng)前顯示的哪一個(gè)元素。  */  private MarginLayoutParams firstItemParams;  /**  * 用于計(jì)算手指滑動(dòng)的速度。  */  private VelocityTracker mVelocityTracker;  /**  * 重寫(xiě)SlidingSwitcherView的構(gòu)造函數(shù),用于允許在XML中引用當(dāng)前的自定義布局。  *  * @param context  * @param attrs  */  public SlidingSwitcherView(Context context, AttributeSet attrs) {  super(context, attrs);  }  /**  * 滾動(dòng)到下一個(gè)元素。  */  public void scrollToNext() {  new ScrollTask().execute(-20);  }  /**  * 滾動(dòng)到上一個(gè)元素。  */  public void scrollToPrevious() {  new ScrollTask().execute(20);  }  /**  * 在onLayout中重新設(shè)定菜單元素和標(biāo)簽元素的參數(shù)。  */  @Override  protected void onLayout(boolean changed, int l, int t, int r, int b) {  super.onLayout(changed, l, t, r, b);  if (changed) {   initializeItems();   initializeDots();  }  }  /**  * 初始化菜單元素,為每一個(gè)子元素增加監(jiān)聽(tīng)事件,并且改變所有子元素的寬度,讓它們等于父元素的寬度。  */  private void initializeItems() {  switcherViewWidth = getWidth();  itemsLayout = (LinearLayout) getChildAt(0);  itemsCount = itemsLayout.getChildCount();  borders = new int[itemsCount];  for (int i = 0; i < itemsCount; i++) {   borders[i] = -i * switcherViewWidth;   View item = itemsLayout.getChildAt(i);   MarginLayoutParams params = (MarginLayoutParams) item.getLayoutParams();   params.width = switcherViewWidth;   item.setLayoutParams(params);   item.setOnTouchListener(this);  }  leftEdge = borders[itemsCount - 1];  firstItem = itemsLayout.getChildAt(0);  firstItemParams = (MarginLayoutParams) firstItem.getLayoutParams();  }  /**  * 初始化標(biāo)簽元素。  */  private void initializeDots() {  dotsLayout = (LinearLayout) getChildAt(1);  refreshDotsLayout();  }  @Override  public boolean onTouch(View v, MotionEvent event) {  createVelocityTracker(event);  switch (event.getAction()) {  case MotionEvent.ACTION_DOWN:   // 手指按下時(shí),記錄按下時(shí)的橫坐標(biāo)   xDown = event.getRawX();   break;  case MotionEvent.ACTION_MOVE:   // 手指移動(dòng)時(shí),對(duì)比按下時(shí)的橫坐標(biāo),計(jì)算出移動(dòng)的距離,來(lái)調(diào)整左側(cè)布局的leftMargin值,從而顯示和隱藏左側(cè)布局   xMove = event.getRawX();   int distanceX = (int) (xMove - xDown) - (currentItemIndex * switcherViewWidth);   firstItemParams.leftMargin = distanceX;   if (beAbleToScroll()) {   firstItem.setLayoutParams(firstItemParams);   }   break;  case MotionEvent.ACTION_UP:   // 手指抬起時(shí),進(jìn)行判斷當(dāng)前手勢(shì)的意圖,從而決定是滾動(dòng)到左側(cè)布局,還是滾動(dòng)到右側(cè)布局   xUp = event.getRawX();   if (beAbleToScroll()) {   if (wantScrollToPrevious()) {    if (shouldScrollToPrevious()) {    currentItemIndex--;    scrollToPrevious();    refreshDotsLayout();    } else {    scrollToNext();    }   } else if (wantScrollToNext()) {    if (shouldScrollToNext()) {    currentItemIndex++;    scrollToNext();    refreshDotsLayout();    } else {    scrollToPrevious();    }   }   }   recycleVelocityTracker();   break;  }  return false;  }  /**  * 當(dāng)前是否能夠滾動(dòng),滾動(dòng)到第一個(gè)或最后一個(gè)元素時(shí)將不能再滾動(dòng)。  *  * @return 當(dāng)前l(fā)eftMargin的值在leftEdge和rightEdge之間返回true,否則返回false。  */  private boolean beAbleToScroll() {  return firstItemParams.leftMargin < rightEdge && firstItemParams.leftMargin > leftEdge;  }  /**  * 判斷當(dāng)前手勢(shì)的意圖是不是想滾動(dòng)到上一個(gè)菜單元素。如果手指移動(dòng)的距離是正數(shù),則認(rèn)為當(dāng)前手勢(shì)是想要滾動(dòng)到上一個(gè)菜單元素。  *  * @return 當(dāng)前手勢(shì)想滾動(dòng)到上一個(gè)菜單元素返回true,否則返回false。  */  private boolean wantScrollToPrevious() {  return xUp - xDown > 0;  }  /**  * 判斷當(dāng)前手勢(shì)的意圖是不是想滾動(dòng)到下一個(gè)菜單元素。如果手指移動(dòng)的距離是負(fù)數(shù),則認(rèn)為當(dāng)前手勢(shì)是想要滾動(dòng)到下一個(gè)菜單元素。  *  * @return 當(dāng)前手勢(shì)想滾動(dòng)到下一個(gè)菜單元素返回true,否則返回false。  */  private boolean wantScrollToNext() {  return xUp - xDown < 0;  }  /**  * 判斷是否應(yīng)該滾動(dòng)到下一個(gè)菜單元素。如果手指移動(dòng)距離大于屏幕的1/2,或者手指移動(dòng)速度大于SNAP_VELOCITY,  * 就認(rèn)為應(yīng)該滾動(dòng)到下一個(gè)菜單元素。  *  * @return 如果應(yīng)該滾動(dòng)到下一個(gè)菜單元素返回true,否則返回false。  */  private boolean shouldScrollToNext() {  return xDown - xUp > switcherViewWidth / 2 || getScrollVelocity() > SNAP_VELOCITY;  }  /**  * 判斷是否應(yīng)該滾動(dòng)到上一個(gè)菜單元素。如果手指移動(dòng)距離大于屏幕的1/2,或者手指移動(dòng)速度大于SNAP_VELOCITY,  * 就認(rèn)為應(yīng)該滾動(dòng)到上一個(gè)菜單元素。  *  * @return 如果應(yīng)該滾動(dòng)到上一個(gè)菜單元素返回true,否則返回false。  */  private boolean shouldScrollToPrevious() {  return xUp - xDown > switcherViewWidth / 2 || getScrollVelocity() > SNAP_VELOCITY;  }  /**  * 刷新標(biāo)簽元素布局,每次currentItemIndex值改變的時(shí)候都應(yīng)該進(jìn)行刷新。  */  private void refreshDotsLayout() {  dotsLayout.removeAllViews();  for (int i = 0; i < itemsCount; i++) {   LinearLayout.LayoutParams linearParams = new LinearLayout.LayoutParams(0,    LayoutParams.FILL_PARENT);   linearParams.weight = 1;   RelativeLayout relativeLayout = new RelativeLayout(getContext());   ImageView image = new ImageView(getContext());   if (i == currentItemIndex) {   image.setBackgroundResource(R.drawable.dot_selected);   } else {   image.setBackgroundResource(R.drawable.dot_unselected);   }   RelativeLayout.LayoutParams relativeParams = new RelativeLayout.LayoutParams(    LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);   relativeParams.addRule(RelativeLayout.CENTER_IN_PARENT);   relativeLayout.addView(image, relativeParams);   dotsLayout.addView(relativeLayout, linearParams);  }  }  /**  * 創(chuàng)建VelocityTracker對(duì)象,并將觸摸事件加入到VelocityTracker當(dāng)中。  *  * @param event  *  右側(cè)布局監(jiān)聽(tīng)控件的滑動(dòng)事件  */  private void createVelocityTracker(MotionEvent event) {  if (mVelocityTracker == null) {   mVelocityTracker = VelocityTracker.obtain();  }  mVelocityTracker.addMovement(event);  }  /**  * 獲取手指在右側(cè)布局的監(jiān)聽(tīng)View上的滑動(dòng)速度。  *  * @return 滑動(dòng)速度,以每秒鐘移動(dòng)了多少像素值為單位。  */  private int getScrollVelocity() {  mVelocityTracker.computeCurrentVelocity(1000);  int velocity = (int) mVelocityTracker.getXVelocity();  return Math.abs(velocity);  }  /**  * 回收VelocityTracker對(duì)象。  */  private void recycleVelocityTracker() {  mVelocityTracker.recycle();  mVelocityTracker = null;  }  /**  * 檢測(cè)菜單滾動(dòng)時(shí),是否有穿越border,border的值都存儲(chǔ)在{@link #borders}中。  *  * @param leftMargin  *  第一個(gè)元素的左偏移值  * @param speed  *  滾動(dòng)的速度,正數(shù)說(shuō)明向右滾動(dòng),負(fù)數(shù)說(shuō)明向左滾動(dòng)。  * @return 穿越任何一個(gè)border了返回true,否則返回false。  */  private boolean isCrossBorder(int leftMargin, int speed) {  for (int border : borders) {   if (speed > 0) {   if (leftMargin >= border && leftMargin - speed < border) {    return true;   }   } else {   if (leftMargin <= border && leftMargin - speed > border) {    return true;   }   }  }  return false;  }  /**  * 找到離當(dāng)前的leftMargin最近的一個(gè)border值。  *  * @param leftMargin  *  第一個(gè)元素的左偏移值  * @return 離當(dāng)前的leftMargin最近的一個(gè)border值。  */  private int findClosestBorder(int leftMargin) {  int absLeftMargin = Math.abs(leftMargin);  int closestBorder = borders[0];  int closestMargin = Math.abs(Math.abs(closestBorder) - absLeftMargin);  for (int border : borders) {   int margin = Math.abs(Math.abs(border) - absLeftMargin);   if (margin < closestMargin) {   closestBorder = border;   closestMargin = margin;   }  }  return closestBorder;  }  class ScrollTask extends AsyncTask<Integer, Integer, Integer> {  @Override  protected Integer doInBackground(Integer... speed) {   int leftMargin = firstItemParams.leftMargin;   // 根據(jù)傳入的速度來(lái)滾動(dòng)界面,當(dāng)滾動(dòng)穿越border時(shí),跳出循環(huán)。   while (true) {   leftMargin = leftMargin + speed[0];   if (isCrossBorder(leftMargin, speed[0])) {    leftMargin = findClosestBorder(leftMargin);    break;   }   publishProgress(leftMargin);   // 為了要有滾動(dòng)效果產(chǎn)生,每次循環(huán)使線程睡眠10毫秒,這樣肉眼才能夠看到滾動(dòng)動(dòng)畫(huà)。   sleep(10);   }   return leftMargin;  }  @Override  protected void onProgressUpdate(Integer... leftMargin) {   firstItemParams.leftMargin = leftMargin[0];   firstItem.setLayoutParams(firstItemParams);  }  @Override  protected void onPostExecute(Integer leftMargin) {   firstItemParams.leftMargin = leftMargin;   firstItem.setLayoutParams(firstItemParams);  }  }  /**  * 使當(dāng)前線程睡眠指定的毫秒數(shù)。  *  * @param millis  *  指定當(dāng)前線程睡眠多久,以毫秒為單位  */  private void sleep(long millis) {  try {   Thread.sleep(millis);  } catch (InterruptedException e) {   e.printStackTrace();  }  } } 

細(xì)心的朋友可以看出來(lái),我還是重用了很多之前的代碼,這里有幾個(gè)重要點(diǎn)我說(shuō)一下。在onLayout方法里,重定義了各個(gè)包含圖片的控件的大小,然后為每個(gè)包含圖片的控件都注冊(cè)了一個(gè)touch事件監(jiān)聽(tīng)器。這樣當(dāng)我們滑動(dòng)任何一樣圖片控件的時(shí)候,都會(huì)觸發(fā)onTouch事件,然后通過(guò)改變第一個(gè)圖片控件的leftMargin,去實(shí)現(xiàn)動(dòng)畫(huà)效果。之后在onLayout里又動(dòng)態(tài)加入了頁(yè)簽View,有幾個(gè)圖片控件就會(huì)加入幾個(gè)頁(yè)簽,然后根據(jù)currentItemIndex來(lái)決定高亮顯示哪一個(gè)頁(yè)簽。其它也沒(méi)什么要特別說(shuō)明的了,更深的理解大家去看代碼和注釋吧。

然后看一下布局文件中如何使用我們自定義的這個(gè)控件,創(chuàng)建或打開(kāi)activity_main.xml,里面加入如下代碼:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  xmlns:tools="http://schemas.android.com/tools"  android:layout_width="fill_parent"  android:layout_height="fill_parent"  android:orientation="horizontal"  tools:context=".MainActivity" >  <com.example.viewswitcher.SlidingSwitcherView  android:id="@+id/slidingLayout"  android:layout_width="fill_parent"  android:layout_height="100dip" >  <LinearLayout   android:layout_width="fill_parent"   android:layout_height="fill_parent"   android:orientation="horizontal" >   <Button   android:layout_width="fill_parent"   android:layout_height="fill_parent"   android:background="@drawable/image1" />   <Button   android:layout_width="fill_parent"   android:layout_height="fill_parent"   android:background="@drawable/image2" />   <Button   android:layout_width="fill_parent"   android:layout_height="fill_parent"   android:background="@drawable/image3" />   <Button   android:layout_width="fill_parent"   android:layout_height="fill_parent"   android:background="@drawable/image4" />  </LinearLayout>  <LinearLayout   android:layout_width="60dip"   android:layout_height="20dip"   android:layout_alignParentBottom="true"   android:layout_alignParentRight="true"   android:layout_margin="15dip"   android:orientation="horizontal" >  </LinearLayout>  </com.example.viewswitcher.SlidingSwitcherView> </LinearLayout> 

 我們可以看到,com.example.viewswitcher.SlidingSwitcherView的根目錄下放置了兩個(gè)LinearLayout。第一個(gè)LinearLayout中要放入需要滾動(dòng)顯示的圖片,這里我們加入了四個(gè)Button,每個(gè)Button都設(shè)置了一張背景圖片。第二個(gè)LinearLayout中不需要加入任何東西,只要控制好大小和位置,標(biāo)簽會(huì)在運(yùn)行的時(shí)候自動(dòng)加入到這個(gè)layout中。

然后創(chuàng)建或打開(kāi)MainActivity作為主界面,里面沒(méi)有加入任何新增的代碼:

public class MainActivity extends Activity {  @Override  protected void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  setContentView(R.layout.activity_main);  } } 

最后是給出AndroidManifest.xml的代碼,也都是自動(dòng)生成的內(nèi)容:

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"  package="com.example.viewswitcher"  android:versionCode="1"  android:versionName="1.0" >  <uses-sdk  android:minSdkVersion="8"  android:targetSdkVersion="8" />  <application  android:allowBackup="true"  android:icon="@drawable/ic_launcher"  android:label="@string/app_name"  android:theme="@android:style/Theme.NoTitleBar" >  <activity   android:name="com.example.viewswitcher.MainActivity"   android:label="@string/app_name" >   <intent-filter>   <action android:name="android.intent.action.MAIN" />   <category android:name="android.intent.category.LAUNCHER" />   </intent-filter>  </activity>  </application> </manifest> 

好了,現(xiàn)在我們來(lái)看下運(yùn)行效果吧,由于手機(jī)壞了,只能在模擬器上運(yùn)行了。

首先是程序打開(kāi)的時(shí)候,界面顯示如下:

android,圖片滾動(dòng),頁(yè)簽

然后手指在圖片上滑動(dòng),我們可以看到圖片滾動(dòng)的效果:

 android,圖片滾動(dòng),頁(yè)簽

不停的翻頁(yè),頁(yè)簽也會(huì)跟著一起改變,下圖中我們可以看到高亮顯示的點(diǎn)是變換的:

 android,圖片滾動(dòng),頁(yè)簽

恩,對(duì)比一下淘寶客戶端的效果,我覺(jué)得我們模仿的還是挺好的。咦,好像少了點(diǎn)什么。。。。。。原來(lái)圖片并不會(huì)自動(dòng)播放。。。。。

沒(méi)關(guān)系,我在后面的一篇文章中補(bǔ)充了自動(dòng)播放這個(gè)功能,而且不僅僅是自動(dòng)播放功能喔,請(qǐng)參考 Android使用自定義屬性實(shí)現(xiàn)圖片自動(dòng)播放滾動(dòng)的功能。

今天的文章就到這里了,有問(wèn)題的朋友請(qǐng)?jiān)谙旅媪粞浴?/p>

源碼下載,請(qǐng)點(diǎn)擊這里

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)VEVB武林網(wǎng)的支持。


注:相關(guān)教程知識(shí)閱讀請(qǐng)移步到Android開(kāi)發(fā)頻道。
發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 南城县| 习水县| 博客| 温宿县| 迁安市| 昌吉市| 五常市| 莒南县| 湘潭市| 宜章县| 锡林郭勒盟| 红桥区| 垫江县| 齐河县| 屏山县| 博客| 科技| 文化| 五莲县| 麻阳| 黄冈市| 延川县| 三原县| 宁强县| 天柱县| 沙坪坝区| 枣庄市| 苍南县| 准格尔旗| 府谷县| 廊坊市| 灵丘县| 时尚| 旌德县| 舟曲县| 宁化县| 靖宇县| 新蔡县| 山阳县| 固安县| 名山县|