带有底点的 Android ViewPager
Posted
技术标签:
【中文标题】带有底点的 Android ViewPager【英文标题】:Android ViewPager with bottom dots 【发布时间】:2014-01-02 09:49:25 【问题描述】:我想在我的 ViewPager 中添加 3 个底部点,就像这样。
我使用 FragmentActivity 并支持库 ViewPager。
【问题讨论】:
我需要添加点,而不是标签(比如 Nexus 5 启动器)。 查看我的解决方案:***.com/a/38459310/4631935 【参考方案1】:不需要那么多代码。
只需使用 viewpager
和 tablayout
,您无需编写太多代码即可完成所有这些工作。
您的主要布局:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_
android:layout_>
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_
android:layout_>
</android.support.v4.view.ViewPager>
<android.support.design.widget.TabLayout
android:id="@+id/tabDots"
android:layout_alignParentBottom="true"
android:layout_
android:layout_
app:tabBackground="@drawable/tab_selector"
app:tabGravity="center"
app:tabIndicatorHeight="0dp"/>
</RelativeLayout>
如下连接您的 UI 元素不活动或片段:
Java 代码:
mImageViewPager = (ViewPager) findViewById(R.id.pager);
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabDots);
tabLayout.setupWithViewPager(mImageViewPager, true);
就是这样,你可以走了。
您需要在 drawable 文件夹中创建以下 xml 资源文件。
tab_indicator_selected.xml
<?xml version="1.0" encoding="utf-8"?>
<shape
android:innerRadius="0dp"
android:shape="ring"
android:thickness="4dp"
android:useLevel="false"
xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/colorAccent"/>
</shape>
tab_indicator_default.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:innerRadius="0dp"
android:shape="oval"
android:thickness="2dp"
android:useLevel="false">
<solid android:color="@android:color/darker_gray"/>
</shape>
tab_selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/tab_indicator_selected"
android:state_selected="true"/>
<item android:drawable="@drawable/tab_indicator_default"/>
</selector>
感觉像我一样懒惰?好了,上面所有的代码都被转换成一个库了!
用法
在您的 gradle 中添加以下内容:
implementation 'com.chabbal:slidingdotsplash:1.0.2'
将以下内容添加到您的 Activity 或 Fragment 布局中。
<com.chabbal.slidingdotsplash.SlidingSplashView
android:id="@+id/splash"
android:layout_
android:layout_
app:imageResources="@array/img_id_arr"/>
在strings.xml
中创建一个整数数组,例如
<integer-array name="img_id_arr">
<item>@drawable/img1</item>
<item>@drawable/img2</item>
<item>@drawable/img3</item>
<item>@drawable/img4</item>
</integer-array>
完成!
Extra 为了收听页面更改,请使用 addOnPageChangeListener(listener);
Github link.
【讨论】:
它将点放在父宽度的中心......是的,点之间也有适当的间距......如果对你有帮助,请投票...... 在我问这个问题之前我已经投票了,因为它已经帮助了我很多,但现在我希望我可以投票 +2 或 +100 :) 因为添加 onPageChangeListener 对我来说非常有效!并且只是为了让任何可能使用它的人清楚,您只需要在 onPageSelected 回调上进行更新,在 onPageScrolled 回调中添加更新会使它变得混乱,它会在您开始滚动时立即开始更新,这不是视觉效果不错! 您想在较新的版本中使用com.google.android.material.tabs.TabLayout
更新:可行,但您应该使用新组件:androidx.viewpager.widget.ViewPager 和 com.google.android.material.tabs.TabLayout
在我将形状更改为 ring
之前,上面的可绘制 XML 看起来很奇怪。似乎在slidingdotsplash库中也使用了环而不是椭圆。【参考方案2】:
我的手工解决方案:
在布局中:
<LinearLayout
android:orientation="horizontal"
android:layout_
android:layout_
android:id="@+id/dots"
/>
在活动中
private final static int NUM_PAGES = 5;
private ViewPager mViewPager;
private List<ImageView> dots;
@Override
protected void onCreate(Bundle savedInstanceState)
// ...
addDots();
public void addDots()
dots = new ArrayList<>();
LinearLayout dotsLayout = (LinearLayout)findViewById(R.id.dots);
for(int i = 0; i < NUM_PAGES; i++)
ImageView dot = new ImageView(this);
dot.setImageDrawable(getResources().getDrawable(R.drawable.pager_dot_not_selected));
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
);
dotsLayout.addView(dot, params);
dots.add(dot);
mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener()
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
@Override
public void onPageSelected(int position)
selectDot(position);
@Override
public void onPageScrollStateChanged(int state)
);
public void selectDot(int idx)
Resources res = getResources();
for(int i = 0; i < NUM_PAGES; i++)
int drawableId = (i==idx)?(R.drawable.pager_dot_selected):(R.drawable.pager_dot_not_selected);
Drawable drawable = res.getDrawable(drawableId);
dots.get(i).setImageDrawable(drawable);
【讨论】:
为什么要使用重组件Layout
只是为了放一个点?请改用View
。
我认为线性布局是为了将点居中和分组。【参考方案3】:
viewPager.addOnPageChangeListener(new OnPageChangeListener()
@Override
public void onPageSelected(int position)
switch (position)
case 0:
img_page1.setImageResource(R.drawable.dot_selected);
img_page2.setImageResource(R.drawable.dot);
img_page3.setImageResource(R.drawable.dot);
img_page4.setImageResource(R.drawable.dot);
break;
case 1:
img_page1.setImageResource(R.drawable.dot);
img_page2.setImageResource(R.drawable.dot_selected);
img_page3.setImageResource(R.drawable.dot);
img_page4.setImageResource(R.drawable.dot);
break;
case 2:
img_page1.setImageResource(R.drawable.dot);
img_page2.setImageResource(R.drawable.dot);
img_page3.setImageResource(R.drawable.dot_selected);
img_page4.setImageResource(R.drawable.dot);
break;
case 3:
img_page1.setImageResource(R.drawable.dot);
img_page2.setImageResource(R.drawable.dot);
img_page3.setImageResource(R.drawable.dot);
img_page4.setImageResource(R.drawable.dot_selected);
break;
default:
break;
@Override
public void onPageScrolled(int arg0, float arg1, int arg2)
@Override
public void onPageScrollStateChanged(int arg0)
);
【讨论】:
setOnPageChangedListener()
已弃用!!立即使用addOnPageChangedListener()
。
这并不是一个很好的解决方案,因为您必须确切地知道您将使用多少页。一旦添加或删除页面,您就有麻烦了。
不要对抗框架。 Android 有执行此操作的工具,如 TabLayout,没有理由重新发明***。
简化为: private void setDotSelection(int index) dot1.setImageResource(index == 0 ? R.drawable.circle_selected : R.drawable.circle); dot2.setImageResource(index == 1 ? R.drawable.circle_selected : R.drawable.circle); dot3.setImageResource(index == 2 ? R.drawable.circle_selected : R.drawable.circle); dot4.setImageResource(index == 3 ? R.drawable.circle_selected : R.drawable.circle); dot5.setImageResource(index == 4 ? R.drawable.circle_selected : R.drawable.circle); 【参考方案4】:
我创建了一个库来满足 ViewPager 中页面指示器的需求。我的库包含一个名为 DotIndicator 的视图。要使用我的库,请将 compile 'com.matthew-tamlin:sliding-intro-screen:3.2.0'
添加到您的 gradle 构建文件中。
可以通过添加以下内容将视图添加到您的布局中:
<com.matthewtamlin.sliding_intro_screen_library.indicators.DotIndicator
android:layout_
android:layout_
app:numberOfDots=YOUR_INT_HERE
app:selectedDotIndex=YOUR_INT_HERE/>
上面的代码完美地复制了谷歌启动器主屏幕上点的功能,但是如果你想进一步自定义它,可以添加以下属性:
app:unselectedDotDiameter
和 app:selectedDotDiameter
设置点的直径
app:unselectedDotColor
和 app:selectedDotColor
设置点的颜色
app:spacingBetweenDots
改变点之间的距离
app:dotTransitionDuration
设置动画从小到大(再返回)的时间
此外,可以使用以下方式以编程方式创建视图:
DotIndicator indicator = new DotIndicator(context);
存在修改属性的方法,类似于属性。要更新指标以显示选定的不同页面,只需从 ViewPager.OnPageChangeListener.onPageSelected(int)
内部调用方法 indicator.setSelectedItem(int, true)
。
这是一个使用示例:
如果您有兴趣,该库实际上是为制作如上图所示的介绍屏幕而设计的。
Github 源代码在这里:https://github.com/MatthewTamlin/SlidingIntroScreen
【讨论】:
在 indicator.setSelectedItem(position, true) 处出错; IndexOutOfBoundsException 哪个版本?我以为我在 3.0.1 中修复了这个问题,你的 indicator.size() 和 position 的值是多少? 很棒的图书馆!但是,是否有更优雅的方式来同时更新 selectedItem 和 numberOfItems ?根据哪个更大,顺序会发生变化,否则会引发 IndexOutOfBounds 异常。见this gist。 @vsp DotIndicator 实现了 SelectionIndicator 接口,这意味着 if 有两个有用的方法可以解决您的问题:int getNumberOfItems()
和 void setNumberOfItems()
。而不是捕捉异常,你可以做这样的事情gist.github.com/MatthewTamlin/761c7338d271af29526edc7859480aef。
对于没有关注要点对话的任何人,实际上存在一个现已修复的错误。 3.0.2 是目前最新的版本。【参考方案5】:
ViewPagerIndicator
自 2012 年以来一直没有更新,并且有几个从未修复的错误。
我终于找到了这个灯光库的替代方案,它可以为viewpager
显示漂亮的点,这是链接:
https://github.com/ongakuer/CircleIndicator
易于实施!
【讨论】:
你知道哪些错误?你能提供一些来源吗? 这两个都是环形 xml 可绘制的。但我将矩形框视为分隔符。应用程序:ci_drawable=。 “@drawable/selected_dot”应用程序:ci_drawable_unselected=。 "@drawable/default_dot" 很容易实现,但即使我只设置了循环器 xml,它也会在选定的情况下显示矩形框。【参考方案6】:我想为上述问题发布一个更简单的解决方案,指标编号可以动态更改,只需更改一个变量值dotCounts=x
我所做的就是这样。
-
在 drawable 文件夹中为名为“item_selected”的页面选定指示器创建一个 xml 文件。
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval" android:useLevel="true"
android:dither="true">
<size android: android:/>
<solid android:color="@color/image_item_selected_for_dots"/>
</shape>
-
为未选中的指标创建一个名为“item_unselected”的 xml 文件
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval" android:useLevel="true"
android:dither="true">
<size android: android:/>
<solid android:color="@color/image_item_unselected_for_dots"/>
</shape>
-
现在将这部分代码添加到您要显示指标的位置,例如在您的 Layout XML 文件中的
viewPager
下方。
<RelativeLayout
android:id="@+id/viewPagerIndicator"
android:layout_
android:layout_below="@+id/banner_pager"
android:layout_
android:gravity="center">
<LinearLayout
android:id="@+id/viewPagerCountDots"
android:layout_
android:layout_
android:layout_centerHorizontal="true"
android:gravity="center"
android:orientation="horizontal" />
</RelativeLayout>
-
将此函数添加到您的活动文件顶部,其中您的布局被夸大或与上述 xml 文件相关
private int dotsCount = 5; //No of tabs or images
private ImageView[] dots;
LinearLayout linearLayout;
private void drawPageSelectionIndicators(int mPosition)
if(linearLayout!=null)
linearLayout.removeAllViews();
linearLayout=(LinearLayout)findViewById(R.id.viewPagerCountDots);
dots = new ImageView[dotsCount];
for (int i = 0; i < dotsCount; i++)
dots[i] = new ImageView(context);
if(i==mPosition)
dots[i].setImageDrawable(getResources().getDrawable(R.drawable.item_selected));
else
dots[i].setImageDrawable(getResources().getDrawable(R.drawable.item_unselected));
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
);
params.setMargins(4, 0, 4, 0);
linearLayout.addView(dots[i], params);
-
最后在您的 onCreate 方法中添加以下代码以引用您的布局并处理页面选择的位置
drawPageSelectionIndicators(0);
mPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener()
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
@Override
public void onPageSelected(int position)
drawPageSelectionIndicators(position);
@Override
public void onPageScrollStateChanged(int state)
);
【讨论】:
【参考方案7】:以下是我提出的解决方案。
由于我们只需要在视图寻呼机中显示一些图像,因此避免了繁琐的片段使用。 实现了查看页面指示器(底部的点,没有任何额外的库或插件) 在触摸视图页面指示器(点)时,页面导航也会发生。 请不要忘记在资源中添加您自己的图片。 随时发表评论和改进。A) 以下是我的 activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_
android:layout_
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="schneider.carouseladventure.MainActivity">
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/viewpager"
android:layout_
android:layout_ />
<RelativeLayout
android:id="@+id/viewPagerIndicator"
android:layout_
android:layout_
android:layout_alignParentBottom="true"
android:layout_marginTop="5dp"
android:gravity="center">
<LinearLayout
android:id="@+id/viewPagerCountDots"
android:layout_
android:layout_
android:layout_centerHorizontal="true"
android:gravity="center"
android:orientation="horizontal" />
</RelativeLayout>
</RelativeLayout>
B) pager_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_
android:layout_>
<ImageView
android:layout_
android:layout_
android:id="@+id/imageView" />
</LinearLayout>
C) MainActivity.java
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
public class MainActivity extends AppCompatActivity implements ViewPager.OnPageChangeListener, View.OnClickListener
int[] mResources = R.drawable.nature1, R.drawable.nature2, R.drawable.nature3, R.drawable.nature4,
R.drawable.nature5, R.drawable.nature6
;
ViewPager mViewPager;
private CustomPagerAdapter mAdapter;
private LinearLayout pager_indicator;
private int dotsCount;
private ImageView[] dots;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mViewPager = (ViewPager) findViewById(R.id.viewpager);
pager_indicator = (LinearLayout) findViewById(R.id.viewPagerCountDots);
mAdapter = new CustomPagerAdapter(this, mResources);
mViewPager.setAdapter(mAdapter);
mViewPager.setCurrentItem(0);
mViewPager.setOnPageChangeListener(this);
setPageViewIndicator();
private void setPageViewIndicator()
Log.d("###setPageViewIndicator", " : called");
dotsCount = mAdapter.getCount();
dots = new ImageView[dotsCount];
for (int i = 0; i < dotsCount; i++)
dots[i] = new ImageView(this);
dots[i].setImageDrawable(getResources().getDrawable(R.drawable.nonselecteditem_dot));
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
);
params.setMargins(4, 0, 4, 0);
final int presentPosition = i;
dots[presentPosition].setOnTouchListener(new View.OnTouchListener()
@Override
public boolean onTouch(View v, MotionEvent event)
mViewPager.setCurrentItem(presentPosition);
return true;
);
pager_indicator.addView(dots[i], params);
dots[0].setImageDrawable(getResources().getDrawable(R.drawable.selecteditem_dot));
@Override
public void onClick(View v)
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
@Override
public void onPageSelected(int position)
Log.d("###onPageSelected, pos ", String.valueOf(position));
for (int i = 0; i < dotsCount; i++)
dots[i].setImageDrawable(getResources().getDrawable(R.drawable.nonselecteditem_dot));
dots[position].setImageDrawable(getResources().getDrawable(R.drawable.selecteditem_dot));
if (position + 1 == dotsCount)
else
@Override
public void onPageScrollStateChanged(int state)
D) CustomPagerAdapter.java
import android.content.Context;
import android.support.v4.view.PagerAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
public class CustomPagerAdapter extends PagerAdapter
private Context mContext;
LayoutInflater mLayoutInflater;
private int[] mResources;
public CustomPagerAdapter(Context context, int[] resources)
mContext = context;
mLayoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mResources = resources;
@Override
public Object instantiateItem(ViewGroup container, int position)
View itemView = mLayoutInflater.inflate(R.layout.pager_item,container,false);
ImageView imageView = (ImageView) itemView.findViewById(R.id.imageView);
imageView.setImageResource(mResources[position]);
/* LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(950, 950);
imageView.setLayoutParams(layoutParams);*/
container.addView(itemView);
return itemView;
@Override
public void destroyItem(ViewGroup collection, int position, Object view)
collection.removeView((View) view);
@Override
public int getCount()
return mResources.length;
@Override
public boolean isViewFromObject(View view, Object object)
return view == object;
E) selecteditem_dot.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval" android:useLevel="true"
android:dither="true">
<size android: android:/>
<solid android:color="#7e7e7e"/>
</shape>
F) 非selecteditem_dot.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval" android:useLevel="true"
android:dither="true">
<size android: android:/>
<solid android:color="#d3d3d3"/>
</shape>
【讨论】:
我用过这个,但在 viewpager 上滚动点没有改变 这是最好的答案。【参考方案8】:@Junaid提供的解决方案可以简化。
您可以直接在TabLayout
上设置可绘制对象,而不是创建选择器 (tab_selector.xml
)。
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_
android:layout_
android:layout_gravity="center"
app:tabBackground="@drawable/tab_indicator_default"
app:tabIndicator="@drawable/tab_indicator_selected"
app:tabIndicatorColor="<youColor>"
app:tabIndicatorGravity="center"
app:tabIndicatorHeight="8dp" />
app:tabIndicatorHeight
设置为与tab_indicator_selected.xml
中相同的值。
按照这种方法,您可以保留TabLayout
的默认动画。
以编程方式,您必须将选项卡设置为选中:
object : ViewPager2.OnPageChangeCallback()
override fun onPageSelected(position: Int)
val tab = binding.tabLayout.getTabAt(position)
binding.tabLayout.selectTab(tab, true)
请注意,我使用了ViewPager2
。应该有使用ViewPager
的等效方法。
【讨论】:
【参考方案9】:如果有人想用缩略图作为指标构建viewPager
,可以选择使用这个库:
ThumbIndicator 用于 viewPager,它也适用于作为资源的图像链接。
【讨论】:
【参考方案10】:我是这样做的,有点类似于上面的解决方案。只需确保在下载所有图像后调用 loadDots() 方法。
private int dotsCount;
private TextView dotsTextView[];
private void setupAdapter()
adapter = new SomeAdapter(getContext(), images);
viewPager.setAdapter(adapter);
viewPager.setCurrentItem(0);
viewPager.addOnPageChangeListener(viewPagerPageChangeListener);
private final ViewPager.OnPageChangeListener viewPagerPageChangeListener = new ViewPager.OnPageChangeListener()
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
@Override
public void onPageSelected(int position)
for (int i = 0; i < dotsCount; i++)
dotsTextView[i].setTextColor(Color.GRAY);
dotsTextView[position].setTextColor(Color.WHITE);
@Override
public void onPageScrollStateChanged(int state)
;
protected void loadDots()
dotsCount = adapter.getCount();
dotsTextView = new TextView[dotsCount];
for (int i = 0; i < dotsCount; i++)
dotsTextView[i] = new TextView(getContext());
dotsTextView[i].setText(R.string.dot);
dotsTextView[i].setTextSize(45);
dotsTextView[i].setTypeface(null, Typeface.BOLD);
dotsTextView[i].setTextColor(android.graphics.Color.GRAY);
mDotsLayout.addView(dotsTextView[i]);
dotsTextView[0].setTextColor(Color.WHITE);
XML
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_>
<android.support.v4.view.ViewPager
android:id="@+id/viewPager"
android:layout_
android:layout_
android:background="#00000000"/>
<ImageView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/introImageView"
android:layout_
android:layout_/>
<LinearLayout
android:id="@+id/image_count"
android:layout_
android:layout_
android:background="#00000000"
android:gravity="center|bottom"
android:orientation="horizontal"/>
</FrameLayout>
【讨论】:
以上是关于带有底点的 Android ViewPager的主要内容,如果未能解决你的问题,请参考以下文章