中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android如何實現圖片滾動和頁簽控件功能

發布時間:2021-04-17 11:04:55 來源:億速云 閱讀:212 作者:小新 欄目:移動開發

這篇文章主要介紹了Android如何實現圖片滾動和頁簽控件功能,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

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

如果你是網購達人,你的手機上一定少不了淘寶客戶端。關注特效的人一定都會發現,淘寶不管是網站還是手機客戶端,主頁上都會有一個圖片滾動播放器,上面展示一些它推薦的商品。這個幾乎可以用淘寶來冠名的功能,看起來還是挺炫的,我們今天就來實現一下。

實現原理其實還是之前那篇文章Android仿人人客戶端滑動菜單的側滑菜單效果,史上最簡單的側滑實現  ,算是以那個原理為基礎的另外一個變種。正所謂一通百通,真正掌握一種方法之后,就可以使用這個方法變換出各種不通的效果。

今天仍然還是實現一個自定義控件,然后我們在任意Activity的布局文件中引用一下,即可實現圖片滾動器的效果。

在Eclipse中新建一個Android項目,項目名就叫做SlidingViewSwitcher。

新建一個類,名叫SlidingSwitcherView,這個類是繼承自RelativeLayout的,并且實現了OnTouchListener接口,具體代碼如下:

public class SlidingSwitcherView extends RelativeLayout implements OnTouchListener { 
 /** 
 * 讓菜單滾動,手指滑動需要達到的速度。 
 */ 
 public static final int SNAP_VELOCITY = 200; 
 /** 
 * SlidingSwitcherView的寬度。 
 */ 
 private int switcherViewWidth; 
 /** 
 * 當前顯示的元素的下標。 
 */ 
 private int currentItemIndex; 
 /** 
 * 菜單中包含的元素總數。 
 */ 
 private int itemsCount; 
 /** 
 * 各個元素的偏移邊界值。 
 */ 
 private int[] borders; 
 /** 
 * 最多可以滑動到的左邊緣。值由菜單中包含的元素總數來定,marginLeft到達此值之后,不能再減少。 
 * 
 */ 
 private int leftEdge = 0; 
 /** 
 * 最多可以滑動到的右邊緣。值恒為0,marginLeft到達此值之后,不能再增加。 
 */ 
 private int rightEdge = 0; 
 /** 
 * 記錄手指按下時的橫坐標。 
 */ 
 private float xDown; 
 /** 
 * 記錄手指移動時的橫坐標。 
 */ 
 private float xMove; 
 /** 
 * 記錄手機抬起時的橫坐標。 
 */ 
 private float xUp; 
 /** 
 * 菜單布局。 
 */ 
 private LinearLayout itemsLayout; 
 /** 
 * 標簽布局。 
 */ 
 private LinearLayout dotsLayout; 
 /** 
 * 菜單中的第一個元素。 
 */ 
 private View firstItem; 
 /** 
 * 菜單中第一個元素的布局,用于改變leftMargin的值,來決定當前顯示的哪一個元素。 
 */ 
 private MarginLayoutParams firstItemParams; 
 /** 
 * 用于計算手指滑動的速度。 
 */ 
 private VelocityTracker mVelocityTracker; 
 /** 
 * 重寫SlidingSwitcherView的構造函數,用于允許在XML中引用當前的自定義布局。 
 * 
 * @param context 
 * @param attrs 
 */ 
 public SlidingSwitcherView(Context context, AttributeSet attrs) { 
 super(context, attrs); 
 } 
 /** 
 * 滾動到下一個元素。 
 */ 
 public void scrollToNext() { 
 new ScrollTask().execute(-20); 
 } 
 /** 
 * 滾動到上一個元素。 
 */ 
 public void scrollToPrevious() { 
 new ScrollTask().execute(20); 
 } 
 /** 
 * 在onLayout中重新設定菜單元素和標簽元素的參數。 
 */ 
 @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(); 
 } 
 } 
 /** 
 * 初始化菜單元素,為每一個子元素增加監聽事件,并且改變所有子元素的寬度,讓它們等于父元素的寬度。 
 */ 
 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(); 
 } 
 /** 
 * 初始化標簽元素。 
 */ 
 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: 
  // 手指按下時,記錄按下時的橫坐標 
  xDown = event.getRawX(); 
  break; 
 case MotionEvent.ACTION_MOVE: 
  // 手指移動時,對比按下時的橫坐標,計算出移動的距離,來調整左側布局的leftMargin值,從而顯示和隱藏左側布局 
  xMove = event.getRawX(); 
  int distanceX = (int) (xMove - xDown) - (currentItemIndex * switcherViewWidth); 
  firstItemParams.leftMargin = distanceX; 
  if (beAbleToScroll()) { 
  firstItem.setLayoutParams(firstItemParams); 
  } 
  break; 
 case MotionEvent.ACTION_UP: 
  // 手指抬起時,進行判斷當前手勢的意圖,從而決定是滾動到左側布局,還是滾動到右側布局 
  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; 
 } 
 /** 
 * 當前是否能夠滾動,滾動到第一個或最后一個元素時將不能再滾動。 
 * 
 * @return 當前leftMargin的值在leftEdge和rightEdge之間返回true,否則返回false。 
 */ 
 private boolean beAbleToScroll() { 
 return firstItemParams.leftMargin < rightEdge && firstItemParams.leftMargin > leftEdge; 
 } 
 /** 
 * 判斷當前手勢的意圖是不是想滾動到上一個菜單元素。如果手指移動的距離是正數,則認為當前手勢是想要滾動到上一個菜單元素。 
 * 
 * @return 當前手勢想滾動到上一個菜單元素返回true,否則返回false。 
 */ 
 private boolean wantScrollToPrevious() { 
 return xUp - xDown > 0; 
 } 
 /** 
 * 判斷當前手勢的意圖是不是想滾動到下一個菜單元素。如果手指移動的距離是負數,則認為當前手勢是想要滾動到下一個菜單元素。 
 * 
 * @return 當前手勢想滾動到下一個菜單元素返回true,否則返回false。 
 */ 
 private boolean wantScrollToNext() { 
 return xUp - xDown < 0; 
 } 
 /** 
 * 判斷是否應該滾動到下一個菜單元素。如果手指移動距離大于屏幕的1/2,或者手指移動速度大于SNAP_VELOCITY, 
 * 就認為應該滾動到下一個菜單元素。 
 * 
 * @return 如果應該滾動到下一個菜單元素返回true,否則返回false。 
 */ 
 private boolean shouldScrollToNext() { 
 return xDown - xUp > switcherViewWidth / 2 || getScrollVelocity() > SNAP_VELOCITY; 
 } 
 /** 
 * 判斷是否應該滾動到上一個菜單元素。如果手指移動距離大于屏幕的1/2,或者手指移動速度大于SNAP_VELOCITY, 
 * 就認為應該滾動到上一個菜單元素。 
 * 
 * @return 如果應該滾動到上一個菜單元素返回true,否則返回false。 
 */ 
 private boolean shouldScrollToPrevious() { 
 return xUp - xDown > switcherViewWidth / 2 || getScrollVelocity() > SNAP_VELOCITY; 
 } 
 /** 
 * 刷新標簽元素布局,每次currentItemIndex值改變的時候都應該進行刷新。 
 */ 
 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); 
 } 
 } 
 /** 
 * 創建VelocityTracker對象,并將觸摸事件加入到VelocityTracker當中。 
 * 
 * @param event 
 *  右側布局監聽控件的滑動事件 
 */ 
 private void createVelocityTracker(MotionEvent event) { 
 if (mVelocityTracker == null) { 
  mVelocityTracker = VelocityTracker.obtain(); 
 } 
 mVelocityTracker.addMovement(event); 
 } 
 /** 
 * 獲取手指在右側布局的監聽View上的滑動速度。 
 * 
 * @return 滑動速度,以每秒鐘移動了多少像素值為單位。 
 */ 
 private int getScrollVelocity() { 
 mVelocityTracker.computeCurrentVelocity(1000); 
 int velocity = (int) mVelocityTracker.getXVelocity(); 
 return Math.abs(velocity); 
 } 
 /** 
 * 回收VelocityTracker對象。 
 */ 
 private void recycleVelocityTracker() { 
 mVelocityTracker.recycle(); 
 mVelocityTracker = null; 
 } 
 /** 
 * 檢測菜單滾動時,是否有穿越border,border的值都存儲在{@link #borders}中。 
 * 
 * @param leftMargin 
 *  第一個元素的左偏移值 
 * @param speed 
 *  滾動的速度,正數說明向右滾動,負數說明向左滾動。 
 * @return 穿越任何一個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; 
 } 
 /** 
 * 找到離當前的leftMargin最近的一個border值。 
 * 
 * @param leftMargin 
 *  第一個元素的左偏移值 
 * @return 離當前的leftMargin最近的一個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; 
  // 根據傳入的速度來滾動界面,當滾動穿越border時,跳出循環。 
  while (true) { 
  leftMargin = leftMargin + speed[0]; 
  if (isCrossBorder(leftMargin, speed[0])) { 
   leftMargin = findClosestBorder(leftMargin); 
   break; 
  } 
  publishProgress(leftMargin); 
  // 為了要有滾動效果產生,每次循環使線程睡眠10毫秒,這樣肉眼才能夠看到滾動動畫。 
  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); 
 } 
 } 
 /** 
 * 使當前線程睡眠指定的毫秒數。 
 * 
 * @param millis 
 *  指定當前線程睡眠多久,以毫秒為單位 
 */ 
 private void sleep(long millis) { 
 try { 
  Thread.sleep(millis); 
 } catch (InterruptedException e) { 
  e.printStackTrace(); 
 } 
 } 
}

細心的朋友可以看出來,我還是重用了很多之前的代碼,這里有幾個重要點我說一下。在onLayout方法里,重定義了各個包含圖片的控件的大小,然后為每個包含圖片的控件都注冊了一個touch事件監聽器。這樣當我們滑動任何一樣圖片控件的時候,都會觸發onTouch事件,然后通過改變第一個圖片控件的leftMargin,去實現動畫效果。之后在onLayout里又動態加入了頁簽View,有幾個圖片控件就會加入幾個頁簽,然后根據currentItemIndex來決定高亮顯示哪一個頁簽。其它也沒什么要特別說明的了,更深的理解大家去看代碼和注釋吧。

然后看一下布局文件中如何使用我們自定義的這個控件,創建或打開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的根目錄下放置了兩個LinearLayout。第一個LinearLayout中要放入需要滾動顯示的圖片,這里我們加入了四個Button,每個Button都設置了一張背景圖片。第二個LinearLayout中不需要加入任何東西,只要控制好大小和位置,標簽會在運行的時候自動加入到這個layout中。

然后創建或打開MainActivity作為主界面,里面沒有加入任何新增的代碼:

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

最后是給出AndroidManifest.xml的代碼,也都是自動生成的內容:

<?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>

好了,現在我們來看下運行效果吧,由于手機壞了,只能在模擬器上運行了。

首先是程序打開的時候,界面顯示如下:

Android如何實現圖片滾動和頁簽控件功能

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

 Android如何實現圖片滾動和頁簽控件功能

不停的翻頁,頁簽也會跟著一起改變,下圖中我們可以看到高亮顯示的點是變換的:

 Android如何實現圖片滾動和頁簽控件功能

恩,對比一下淘寶客戶端的效果,我覺得我們模仿的還是挺好的。咦,好像少了點什么。。。。。。原來圖片并不會自動播放。。。。。

沒關系,我在后面的一篇文章中補充了自動播放這個功能,而且不僅僅是自動播放功能喔,請參考 Android使用自定義屬性實現圖片自動播放滾動的功能。

感謝你能夠認真閱讀完這篇文章,希望小編分享的“Android如何實現圖片滾動和頁簽控件功能”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,更多相關知識等著你來學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

内丘县| 家居| 卢湾区| 奉节县| 西畴县| 仪陇县| 凌海市| 乐亭县| 南宫市| 连平县| 龙山县| 法库县| 玛多县| 勐海县| 西丰县| 舟曲县| 新余市| 昭平县| 新乡县| 中西区| 富锦市| 宝兴县| 伊宁县| 临猗县| 馆陶县| 依兰县| 远安县| 怀化市| 宜宾县| 汉寿县| 巴南区| 襄城县| 芜湖县| 睢宁县| 通道| 措美县| 施秉县| 凤台县| 凉城县| 久治县| 广州市|