如何创建带有点指示器的 Android View Pager?
Posted
技术标签:
【中文标题】如何创建带有点指示器的 Android View Pager?【英文标题】:How do you create an Android View Pager with a dots indicator? 【发布时间】:2016-11-22 09:28:04 【问题描述】:可能你们中的许多人(如我)在创建带有底部圆点的ViewPager
时遇到问题,如下所示:
如何创建这样的 android ViewPager?
【问题讨论】:
2021 如果你是纯 ViewPager2 和 24+,TabLayoutMediator 的关键代码已经改变。我在下面的答案中输入了最新信息。什么皮塔饼! 【参考方案1】:我们只需要:ViewPager、TabLayout 和 2 个用于选定点和默认点的可绘制对象。
首先,我们必须将TabLayout
添加到我们的屏幕布局中,并将其与ViewPager
连接起来。我们可以通过两种方式做到这一点:
嵌套在TabLayout
中ViewPager
<androidx.viewpager.widget.ViewPager
android:id="@+id/photos_viewpager"
android:layout_
android:layout_>
<com.google.android.material.tabs.TabLayout
android:layout_
android:layout_/>
</androidx.viewpager.widget.ViewPager>
在这种情况下
TabLayout
将自动与ViewPager
连接,但TabLayout
将在ViewPager
旁边,而不是在它之上。
单独的TabLayout
<androidx.viewpager.widget.ViewPager
android:id="@+id/photos_viewpager"
android:layout_
android:layout_/>
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
android:layout_
android:layout_/>
在这种情况下,我们可以将
TabLayout
放在任何地方,但我们必须以编程方式将TabLayout
与ViewPager
连接起来
ViewPager pager = (ViewPager) view.findViewById(R.id.photos_viewpager);
PagerAdapter adapter = new PhotosAdapter(getChildFragmentManager(), photosUrl);
pager.setAdapter(adapter);
TabLayout tabLayout = (TabLayout) view.findViewById(R.id.tab_layout);
tabLayout.setupWithViewPager(pager, true);
创建布局后,我们必须准备点。所以我们创建了三个文件:selected_dot.xml
、default_dot.xml
和 tab_selector.xml
。
selected_dot.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape
android:innerRadius="0dp"
android:shape="ring"
android:thickness="8dp"
android:useLevel="false">
<solid android:color="@color/colorAccent"/>
</shape>
</item>
</layer-list>
default_dot.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape
android:innerRadius="0dp"
android:shape="ring"
android:thickness="8dp"
android:useLevel="false">
<solid android:color="@android:color/darker_gray"/>
</shape>
</item>
</layer-list>
tab_selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/selected_dot"
android:state_selected="true"/>
<item android:drawable="@drawable/default_dot"/>
</selector>
现在我们只需要在 XML 布局中的 TabLayout
中添加 3 行代码。
app:tabBackground="@drawable/tab_selector"
app:tabGravity="center"
app:tabIndicatorHeight="0dp"
【讨论】:
你最好把它放在文档部分。不过还是有帮助的。 @ErfanGholampour ***.com/documentation/android/692/viewpager/22945/… @AmitSingh 你可以在 TabLayout 中使用app:tabMaxWidth
我不知道我是否没有正确遵循说明,但我还必须添加以下内容,以便仅显示点之间有适当空间的点:app:tabMaxWidth="30dp" app:tabTextColor="@color/transparent2" app:tabSelectedTextColor="@color/transparent2" app:tabIndicatorHeight="0dp" android:layout_gravity="bottom|center"
用于删除点之间的间距 app:tabPaddingStart="@dimen/margin_7.5" app:tabPaddingEnd="@dimen/margin_7.5"【参考方案2】:
首先创建一个布局,其中为显示在您的视图分页器上的点提供一个 LinerLayout
<RelativeLayout
android:layout_
android:layout_>
<android.support.v4.view.ViewPager
android:id="@+id/view_pager"
android:layout_
android:layout_/>
<LinearLayout
android:id="@+id/pager_dots"
android:layout_
android:layout_
android:layout_alignParentBottom="true"
android:layout_marginBottom="10dp"
android:background="@android:color/transparent"
android:gravity="center_horizontal"
android:orientation="horizontal">
</LinearLayout>
</RelativeLayout>
之后创建 2 个可绘制对象
1.未选中的可绘制对象
<?xml version="1.0" encoding="utf-8"?>
<shape android:shape="oval" xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@android:color/transparent"/>
<size android: android:/>
<stroke android: android:color="#ffffff"/>
</shape>
2。选定的可绘制对象
<?xml version="1.0" encoding="utf-8"?>
<shape android:shape="oval" xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@android:color/transparent"/>
<size android: android:/>
<stroke android: android:color="#000000"/>
</shape>
之后设置适配器
private LinearLayout llPagerDots;
private ViewPager viewPager;
private ArrayList<String> eventImagesUrl;
private HomeViewPagerAdapter homeViewPagerAdapter;
private ImageView[] ivArrayDotsPager;
public void setUpViewPager()
viewPager = (ViewPager) findViewById(R.id.view_pager);
llPagerDots = (LinearLayout) findViewById(R.id.pager_dots);
homeViewPagerAdapter = new HomeViewPagerAdapter(mContext, eventImagesUrl);
viewPager.setAdapter(homeViewPagerAdapter);
setupPagerIndidcatorDots();
ivArrayDotsPager[0].setImageResource(R.drawable.page_indicator_selected);
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener()
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
@Override
public void onPageSelected(int position)
for (int i = 0; i < ivArrayDotsPager.length; i++)
ivArrayDotsPager[i].setImageResource(R.drawable.page_indicator_unselected);
ivArrayDotsPager[position].setImageResource(R.drawable.page_indicator_selected);
@Override
public void onPageScrollStateChanged(int state)
);
创建方法 setupPagerIndidcatorDots() :
private void setupPagerIndidcatorDots()
ivArrayDotsPager = new ImageView[eventImagesUrl.size()];
for (int i = 0; i < ivArrayDotsPager.length; i++)
ivArrayDotsPager[i] = new ImageView(getActivity());
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.setMargins(5, 0, 5, 0);
ivArrayDotsPager[i].setLayoutParams(params);
ivArrayDotsPager[i].setImageResource(R.drawable.page_indicator_unselected);
//ivArrayDotsPager[i].setAlpha(0.4f);
ivArrayDotsPager[i].setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View view)
view.setAlpha(1);
);
llPagerDots.addView(ivArrayDotsPager[i]);
llPagerDots.bringToFront();
【讨论】:
什么是HomeViewPagerAdapter
?
修改 onPageSelect 以使其完美工作,如下所示覆盖 fun onPageSelected(position: Int) for (i in 0 until ivArrayDotsPager!!.size) if (i != position) ivArrayDotsPager!![i ].setImageDrawable(ContextCompat.getDrawable(applicationContext, R.drawable.unselected_dot)) 否则 ivArrayDotsPager!![i].setImageDrawable(ContextCompat.getDrawable(applicationContext, R.drawable.selected_dot)) linearLayout!!.bringToFront()
【参考方案3】:
您可以查看我的图书馆来处理您的请求:https://github.com/tommybuonomo/dotsindicator
在您的 XML 布局中
<com.tbuonomo.viewpagerdotsindicator.DotsIndicator
android:id="@+id/dots_indicator"
android:layout_
android:layout_
android:layout_centerHorizontal="true"
app:dotsColor="@color/colorPrimary"
app:dotsSize="16dp"
app:dotsWidthFactor="3"
/>
在您的 Java 代码中
dotsIndicator = (DotsIndicator) findViewById(R.id.dots_indicator);
viewPager = (ViewPager) findViewById(R.id.view_pager);
adapter = new ViewPagerAdapter();
viewPager.setAdapter(adapter);
dotsIndicator.setViewPager(viewPager);
【讨论】:
使用起来非常简单,尤其是动画看起来很棒......谢谢!但是,我确实必须将tools:replace="android:label"
添加到我的AndroidManifest.xml
中的application
标记中,以消除该标记被定义两次并导致gradle 构建失败的错误。
我收到 gradle
错误,但 Error:(39, 20) All com.android.support libraries must use the exact same version specification (mixing versions can lead to runtime crashes). Found versions 27.0.0, 25.3.1. Examples include 'com.android.support:support-compat:27.0.0' and 'com.android.support:animated-vector-drawable:25.3.1'
- 我该如何解决?
如何自动滚动DotsIndicator
嘿..我实现了这个..我使用了 当您想要使用最新的 ViewPager2 和 Kotlin
进行类似的操作时一切不言自明,无需解释!
1.您的活动或片段
val imageList = listOf(
ImageModel(R.drawable.offer1),
ImageModel(R.drawable.splash),
ImageModel(R.drawable.offer1),
ImageModel(R.drawable.splash2)
)
val adapter = HomeOffersAdapter()
adapter.setItem(imageList)
photos_viewpager.adapter = adapter
TabLayoutMediator(tab_layout, photos_viewpager) tab, position ->
.attach()
2。布局
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_
android:layout_>
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/photos_viewpager"
android:layout_
android:layout_ />
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
android:layout_
android:layout_
android:layout_alignParentBottom="true"
android:layout_gravity="bottom|center"
app:tabBackground="@drawable/tab_selector"
app:tabGravity="center"
app:tabIndicatorHeight="0dp"
app:tabSelectedTextColor="@android:color/transparent"
app:tabTextColor="@android:color/transparent" />
3.可绘制:tab_selector.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/dot_selected" android:state_selected="true" />
<item android:drawable="@drawable/dot_default" />
4.可绘制:dot_selected.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:innerRadius="0dp"
android:shape="ring"
android:thickness="@dimen/dp_8"
android:useLevel="false">
<solid android:color="@color/colorPrimary" />
<stroke
android:
android:color="@android:color/white" />
5.可绘制:dot_default.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:innerRadius="0dp"
android:shape="ring"
android:thickness="@dimen/dp_8"
android:useLevel="false">
<solid android:color="@android:color/transparent" />
<stroke
android:
android:color="@android:color/white" />
6.适配器
class HomeOffersAdapter : RecyclerView.Adapter<HomeOffersAdapter.HomeOffersViewHolder>()
private var list: List<ImageModel> = listOf()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeOffersViewHolder
return HomeOffersViewHolder(parent)
override fun onBindViewHolder(holder: HomeOffersViewHolder, position: Int)
holder.bind(list[position])
fun setItem(list: List<ImageModel>)
this.list = list
notifyDataSetChanged()
override fun getItemCount(): Int = list.size
class HomeOffersViewHolder constructor(itemView: View) : RecyclerView.ViewHolder(itemView)
constructor(parent: ViewGroup) : this(
LayoutInflater.from(parent.context).inflate(
R.layout.pager_item,
parent, false
)
)
fun bind(imageModel: ImageModel)
itemView.offerImage.setImageResource(imageModel.image)
7.布局:pager_item.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_
android:layout_
android:fitsSystemWindows="true"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/offerImage"
android:layout_
android:layout_
android:adjustViewBounds="true"
android:scaleType="fitXY"
tools:src="@drawable/offer1" />
【讨论】:
【参考方案5】:2021,如何实际做到这一点。仅限 ViewPager2。
参考这篇优秀的短文,有几个问题:
https://medium.com/@adrian.kuta93/android-viewpager-with-dots-indicator-a34c91e59e3a
从 2021 年开始的普通 Android Studio 默认项目开始,具有相当新的最小值(目前为 24 个)...
一般概念:
制作一个标准的 TabLayout,但将每个“标签单元”替换为“一个小点”而不是通常的文本。
在 TabLayout 中,您确实可以使用“tabBackground”替换每个“标签单元”:
app:tabBackground="@drawable/tab_selector"
因此,将以下内容添加到具有 ViewPager2 的屏幕的 XML 中:
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
android:layout_
android:layout_
android:layout_gravity="bottom|center"
android:background="#00FFFFFF"
app:tabBackground="@drawable/tab_selector"
app:tabIndicatorGravity="center"
app:tabIndicatorHeight="0dp"/>
请注意,我们正在将 TabLayout 中的每一个“标签单元”替换为我们自己的 “tab_selector”。
明确地说,“tabBackground”是指单个小“标签单元”,不是整个标签栏系统。
(另外,请注意 tabIndicatorGravity 和 tabIndicatorHeight 这两个技巧确实会摆脱通常的“标签单元”的“框”。)
接下来以显而易见的方式创建三个可绘制对象,tab_selector 和两个不同的点。请参阅上述文章或此页面上的 100 个示例。
魔法密码:
在您的onCreate
中有预期的代码...
viewPager = findViewById(R.id.simple_slide_pager);
tab_layout = findViewById(R.id.tab_layout);
viewPager.setAdapter(new ScreenSlidePagerAdapter(this));
最后是让它工作的神奇代码片段。按照上述方式:
2021 年最新:
TabLayoutMediator tabLayoutMediator =
new TabLayoutMediator(tab_layout, viewPager, true,
new TabLayoutMediator.TabConfigurationStrategy()
@Override public void onConfigureTab(
@NonNull TabLayout.Tab tab, int position)
);
tabLayoutMediator.attach();
完成了。
(在onConfigureTab
中,你可以做任何声音效果或任何可能需要的东西。对于更短的语法,请参阅上面@F1iX 的关键评论。)
【讨论】:
这是一个很大的帮助,谢谢。当我在两个 DOT.XML 文件中设置<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_
android:layout_
android:orientation="vertical">
<androidx.viewpager.widget.ViewPager
android:id="@+id/vpImage"
android:layout_
android:layout_ />
<com.google.android.material.tabs.TabLayout
android:id="@+id/tlImage"
android:layout_
android:layout_
app:tabBackground="@drawable/selector_product_image"
app:tabGravity="center"
app:tabIndicatorHeight="0dp"
app:tabMaxWidth="12dp"
app:tabRippleColor="@null" />
</androidx.appcompat.widget.LinearLayoutCompat>
ImageAdapter imageAdapter = new ImageAdapter(getActivity(), arrayListSlider);
binding.vpImage.setOffscreenPageLimit(1);
binding.vpImage.setAdapter(imageAdapter);
binding.tlImage.setupWithViewPager(binding.vpImage);
selector_product_image.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/image_selected" android:state_selected="true" />
<item android:drawable="@drawable/image_unselected" />
</selector>
image_selected.xml
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape
android:innerRadius="0dp"
android:shape="ring"
android:thickness="4dp"
android:useLevel="false">
<solid android:color="@color/colorAccent" />
</shape>
</item>
</layer-list>
image_unselected.xml
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape
android:innerRadius="0dp"
android:shape="ring"
android:thickness="4dp"
android:useLevel="false">
<solid android:color="@color/colorPrimary" />
</shape>
</item>
</layer-list>
ImageAdapter.java
class ImageAdapter extends PagerAdapter
private Context context;
private ArrayList<ImageModel> arrayList;
private LayoutInflater layoutInflater;
public ImageAdapter(Context context, ArrayList<ImageModel> arrayList)
this.context = context;
this.arrayList = arrayList;
this.layoutInflater = (LayoutInflater) this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
@Override
public int getCount()
return arrayList.size();
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object o)
return view == ((View) o);
@Override
public Object instantiateItem(ViewGroup container, int position)
View view = layoutInflater.inflate(R.layout.row_slider_image, container, false);
AppCompatImageView ivProductImage = view.findViewById(R.id.ivProductImage);
if (!TextUtils.isEmpty(arrayList.get(position).getImage()))
Glide.with(context)
.load(arrayList.get(position).getImage())
.apply(new RequestOptions().placeholder(R.drawable.no_image).error(R.drawable.no_image))
.into(ivProductImage);
container.addView(view);
return view;
@Override
public void destroyItem(ViewGroup container, int position, Object object)
container.removeView((View) object);
row_slider_image.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/ivProductImage"
android:layout_
android:layout_
android:adjustViewBounds="true"
android:scaleType="fitXY"
android:src="@drawable/no_image" />
</androidx.appcompat.widget.LinearLayoutCompat>
【讨论】:
【参考方案7】:你的 xml
<RelativeLayout
android:id="@+id/rl_speed"
android:layout_
android:layout_
android:layout_above="@+id/ll_dashboard_buttons"
android:layout_below="@+id/ib_menu">
<com.smart.gps.speedometer.app.utils.SmartViewPager
android:id="@+id/view_pager"
android:layout_
android:layout_
app:layout_behavior="@string/appbar_scrolling_view_behavior">
</com.smart.gps.speedometer.app.utils.SmartViewPager>
<android.support.design.widget.TabLayout
android:id="@+id/sliding_tabs"
android:layout_alignParentBottom="true"
android:layout_
android:layout_
app:tabBackground="@drawable/tab_selector"
app:tabIndicatorHeight="0dp"
app:tabGravity="center"
/>
创建一个drawable。右键单击drawable - > new - > Drawable文件资源 命名该文件
tab_selector.xml
<item android:drawable="@drawable/selected_tab"
android:state_selected="true"/>
<item android:drawable="@drawable/unselected_tab"/>
现在还有两个 xml 文件。再创建两个具有尊重名称的 xml 文件。这些是选择器指示器和未选择指示器
selected_tab.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape
android:innerRadius="0dp"
android:shape="ring"
android:thickness="4dp"
android:useLevel="false">
<solid android:color="@color/highspeed"/>
</shape>
</item>
</layer-list>
unselected_tab.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape
android:innerRadius="0dp"
android:shape="ring"
android:thickness="2dp"
android:useLevel="false">
<solid android:color="@android:color/darker_gray"/>
</shape>
</item>
</layer-list>
【讨论】:
可能SmartViewPager
是您的自定义视图。【参考方案8】:
将 ViewFlipper 和 viewFlipper_linear_dot_lay(Linearlayout) 放在同一基线上,然后按照下面的方法进行
viewFlipper_linear_dot_lay= (LinearLayout) findViewById(R.id.dots_lay);
setupDotsOnViewPager(images_viewFlipper);
for (int i = 0; i < images_viewFlipper.size(); i++)
//Add Images to ViewFlipper
private void setupDotsOnViewPager(ArrayList images_viewFlipper)
images_linear = new ImageView[images_viewFlipper.size()];
for (int i = 0; i < images_linear.length; i++)
images_linear[i] = new ImageView(this);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.setMargins(5, 0, 5, 0);
params.gravity = Gravity.BOTTOM | Gravity.CENTER;
images_linear[i].setLayoutParams(params);
images_linear[i].setImageResource(R.drawable.unselected);
viewFlipper_linear_dot_lay.addView(images_linear[i]);
viewFlipper_linear_dot_lay.bringToFront();
OnRight & OnLeft getsures 放置下面的代码
for (int i = 0; i < images_linear.length; i++)
images_linear[i].setImageResource(R.drawable.unselected);
images_linear[viewFlipper.getDisplayedChild()].setImageResource(R.drawable.selected);
【讨论】:
【参考方案9】:添加依赖项 > 同步 Gradle
implementation 'com.tbuonomo.andrui:viewpagerdotsindicator:4.1.2'
在你的 java 代码中
dotsIndicator = (DotsIndicator) findViewById(R.id.dots_indicator3);
myViewPagerAdapter = new MyViewPagerAdapter();
viewPager.setAdapter(myViewPagerAdapter);
viewPager.addOnPageChangeListener(viewPagerPageChangeListener);
dotsIndicator.setViewPager(viewPager);
在你的布局中
<com.tbuonomo.viewpagerdotsindicator.SpringDotsIndicator
android:id="@+id/spring_dots_indicator"
android:layout_
android:layout_
app:dampingRatio="0.5"
app:dotsColor="@color/material_white"
app:dotsStrokeColor="@color/material_yellow"
app:dotsCornerRadius="2dp"
app:dotsSize="16dp"
app:dotsSpacing="6dp"
app:dotsStrokeWidth="2dp"
app:stiffness="300"
/>
【讨论】:
不错,但这使用的是 AndroidX。我现在无法将我的项目迁移到 AndroidX。 @meekash55 我更喜欢你在开始构建应用程序时迁移到android X,因为有很多案例让你应该迁移到Android X,这个案例是示例之一。 我认为迁移不会花费太长时间,但这是您的决定。适应你的情况和条件以上是关于如何创建带有点指示器的 Android View Pager?的主要内容,如果未能解决你的问题,请参考以下文章
带有 Flutter 更新的 Android Studio 导致了索引循环