自从Gallery被谷歌废弃以后,Google推荐使用ViewPager和HorizontalScrollView来实现Gallery的效果。的确HorizontalScrollView可以实现Gallery的效果。
其实HorizontalScrollView与ScrollView使用的模式一样,里面的内容必须包含在一个布局里面比如LinearLayout等,不能在其标签内直接放入TextView等子控件,必须被包裹。
那么我们上图看看我们最终要实现的效果图:
我们在单个viewPager显示页面放入要获取的新闻网址用于区分功能实现效果。其参数(新闻网址)是创建的时候就传进入进去的,避免写死后,不能增加横向滑动菜单数,看看网易旁边的按钮。如下图:
这样方便我们扩展新闻,毕竟你不可能,也不应该对每一个横向标题固定创建一个Fragment那将是耗时且低效率的,我们选择动态增加。好了,我们开始实现我们应该实现的功能吧。
编程中代码都是次要的,思路才是最重要的,没有思路,俗称码农。先思路后代码才能称为程序员。
看着这个横向滑动菜单,你想到了什么呢?会用到哪些技术?
首先我们可以看到,要想实现这个无限添加的横向滑动的菜单,和怎么都不变的下划线滑动效果,这里最重要的就是算法了。现在默认只有五个菜单,如果你用if else if或者switch case那么,当有100个横向滑动菜单的时候,你不会if100次吧,也不会case100次吧!这里我们先来讲讲这高深莫测的算法。(如果你数据结构里面的算法全部都掌握了,你可以直接跳过,这里用到的算法,就你可以把它想成是一个链表,下划线就是指针,指向哪里。其实就这么简单)
Ⅰ图解算法,从左到右:
①从头条标题滑动到宜昌标题
其结果并不是想象中的滑动宜昌标题的间距,而是滑过头条,也就是说头条滑动到宜昌其实滑动的距离是头条控件的大小。
②同理,从头条直接滑动到轻松一刻。也是滑动的头条加宜昌控件的大小和控件的间距的和。
我们假设两个字的控件大小为90,四个字的控件大小为150,那么①滑动了90,②滑动了180。
我们知道每当ViewPager变动的时候其会触发ViewPager.OnPageChangeListener接口。当我们滑动到某个控件(不特别说明就是横向滑动菜单的TextView控件)的时候,该接口下的onPageSelected(int position)会告诉我们所引值。在这里我们可以设置下划线的滑动效果。
还有因为我们不知道是从哪个标题滑动到哪个标题,我们必须在自定义的HorizontalScrollView控件中增加一个成员变量,保存当前菜单的位置,也就是滑动的时候是从哪个控件开始滑动的。我们这里用到的变量为(mLastPosition)。
我们滑动下划线,用到的是平移动画,因为只是水平滑动,用到的只有两个参数,一个是滑动的起点,另一个就是滑动的终点。所以计算起点和终点,会用到两个循环。算法的代码如下:
for (int j = 0; j < mLastPosition; j++) { scrollStartX += this.titleScroll.get(j).getWidth() + ApplyUtils.dip2px(getContext(), 20);//控件大小+控件间距(dp转换成px) } for (int j=0;j<i;j++){ scrollEndX=scrollEndX + this.titleScroll.get(j).getWidth() + ApplyUtils.dip2px(getContext(), 20); } Log.i("liyuanjinglyj", "scrollStartX=" + String.valueOf(scrollStartX) + "----scrollEndX" + String.valueOf(scrollEndX)); slideview(scrollStartX, scrollEndX);
Ⅱ图解算法,从右到左:
①从科技菜单到财经菜单
前头我们假设过控件的大小,这里我们直接说结果,从科技到财经滑动了90,但是滑动的值是从科技的开头,也就是前面四个控件的大小为420,到财经开头,也就是330。平移动画的前二个参数就是(420,330)。
②从科技菜单到宜昌菜单
同理,也就是科技开头为前面4个控件大小加间距的和为420,到宜昌开头为头条大小90。可以看出来,这里我们也需要2个循环,计算控件离屏幕左边的距离。
算法代码如下:
for (int j = 0; j <mLastPosition; j++) { scrollStartX += this.titleScroll.get(j).getWidth() + ApplyUtils.dip2px(getContext(), 20);//控件大小+控件间距(dp转换成px) } for (int j=0;j<i;j++){ scrollEndX = scrollEndX + this.titleScroll.get(j).getWidth() + ApplyUtils.dip2px(getContext(), 20); } Log.i("liyuanjinglyjfanxiang", "scrollStartX=" + String.valueOf(scrollStartX) + "----scrollEndX" + String.valueOf(scrollEndX)); slideview(scrollStartX, scrollEndX);
怎么设计我们的HorizontalScrollView呢?
我们知道当我们点击HorizontalScrollView中的标题也就是TextView的时候,会触发ViewPager的跳转。而HorizontalScrollView是自定义的空间,当其点击之中的TextView时候,怎么让NewsFragment中的ViewPager跳转呢?
答案很简单,设置一个回调函数,当点击TextView默认会调用实现回调函数的具体方法。
那么我们开始设计我们的HorizontalScrollView。
Ⅰ首先,我们需要用到哪些成员变量
代码如下:
private LayoutInflater inflater;//加载布局进来用的 private View view;//布局View private LinearLayout titleAllTxt;//标题布局 private List<TextView> titleScroll = new ArrayList<>();//标题数组 private ImageView iv_nav_indicator;//下划线 private static int mLastPosition = 0;//当前选中标题索引 private OnItemClickListener mOnClickListener;//监听函数
说明:
LayoutInflater:加载所有的标题也就是TextView到HorizontalScrollView的横向滑动菜单中,使其可以滑动。
titleScroll:HorizontalScrollView每增加一个TextView默认添加到List<TextView>中。
mLastPosition:方法一中讲过,记录当前TextView的索引。
mOnClickListener:回调接口,通知viewPager跳转。
iv_nav_indicator:下划线控件。
view:添加到HorizontalScrollView的布局文件。
titleAllTxt:除下划线外的布局文件,也就是所有的标题。
Ⅱ定义回调接口,通知viewPager跳转
代码如下:
public interface OnItemClickListener { void onClick(int pos); }
ⅢHorizontalScrollView实现View.OnClickListener接口
代码如下:
@Override public void onClick(View v) { mOnClickListener.onClick(titleAllTxt.indexOfChild(v)); }
其作用是方便设置TextView标题的点击事件。
Ⅳ初始化HorizontalScrollView滑动菜单界面
代码如下:
/** * 初始化加载进来的布局 */ private void initView() { this.inflater = LayoutInflater.from(getContext()); view = this.inflater.inflate(R.layout.news_title_horizontalscrollview_layout, null); this.titleAllTxt = (LinearLayout) view.findViewById(R.id.news_title_horizontalscrollview_titletxt_layout); for (int i = 0; i < this.titleAllTxt.getChildCount(); i++) { this.titleScroll.add((TextView) this.titleAllTxt.getChildAt(i)); ((TextView) this.titleAllTxt.getChildAt(i)).setOnClickListener(this); } this.iv_nav_indicator = (ImageView) view.findViewById(R.id.iv_nav_indicator); int w = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); int h = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); this.titleScroll.get(0).measure(w, h); LinearLayout.LayoutParams imageParams = (LinearLayout.LayoutParams) this.iv_nav_indicator.getLayoutParams(); imageParams.width = this.titleScroll.get(0).getMeasuredWidth() + 20; imageParams.height = 5; this.iv_nav_indicator.setLayoutParams(imageParams); addView(view); }
其作用初始化界面,使我们看到如本博文开头的界面,并设置TextView的点击事件。
Ⅴ下划线滑动动画
代码如下:
/** * 滑动动画 * @param p1 起始 * @param p2 终点 */ public void slideview(float p1, float p2) { TranslateAnimation animation = new TranslateAnimation(p1, p2, 0, 0); animation.setInterpolator(new OvershootInterpolator()); animation.setDuration(300); animation.setFillEnabled(true); animation.setFillAfter(true); iv_nav_indicator.startAnimation(animation); }
默认两个参数,一个起点,一个终点。这里与前面两篇博文XML动画形成鲜明的对比,这就是动态加载动画。两种动画实现方式都讲到了。
Ⅵ动态添加标题
代码如下:
/** * 动态添加标题 * @param text 标题文字 * @param context 上下文 */ public void addTextViewTitle(String text, Context context) { TextView textView = new TextView(context); textView.setText(text); textView.setTextColor(Color.BLACK); textView.setClickable(true); textView.setTextSize(20.0f); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); params.setMargins(10, 0, 10, 0); textView.setLayoutParams(params); textView.setOnClickListener(this); this.titleAllTxt.addView(textView); this.titleScroll.add(textView); new Handler().post(new Runnable() { public void run() { try { Thread.sleep(10); int left = titleAllTxt.getMeasuredWidth() - getWidth(); if (left < 0) { left = 0; } scrollTo(left, 0); } catch (Exception e) { e.printStackTrace(); } } }); }
这里为什么要延迟10毫秒后执行,因为刚添加一个TextView标题到HorizontalScrollView中,界面还没来的及刷新,你这个时候如果直接跳转到标题位置的末端。那么就会出现跳转到新添加标题位置的前面那个标题,也就是刚才整个标题栏的大小,后出现界面更新,虽然肉眼觉得这是一起完成的,其实是先后错位了,但你眼睛没有发觉,但程序很诚实。
Ⅶviewpager跳转后下划线联动
代码如下:
/** * viewPager跳转之下划线联动方法 * @param position 跳转后的索引 */ public void setPagerChangeListenerToTextView(int position) { int scrollStartX = 0;//动画其实位置 int scrollEndX = 0;//动画结束位置 for (int i = 0; i < this.titleScroll.size(); i++) {//循环是为了设置其他标题为黑色 if (i == position) { if (mLastPosition < i) {//判断滑动方向,里面为左向右 for (int j = 0; j < mLastPosition; j++) { scrollStartX += this.titleScroll.get(j).getWidth() + ApplyUtils.dip2px(getContext(), 20); } for (int j=0;j<i;j++){ scrollEndX=scrollEndX + this.titleScroll.get(j).getWidth() + ApplyUtils.dip2px(getContext(), 20); } Log.i("liyuanjinglyj", "scrollStartX=" + String.valueOf(scrollStartX) + "----scrollEndX" + String.valueOf(scrollEndX)); slideview(scrollStartX, scrollEndX); } else {//判断滑动方向,里面为右向左 for (int j = 0; j <mLastPosition; j++) { scrollStartX += this.titleScroll.get(j).getWidth() + ApplyUtils.dip2px(getContext(), 20); } for (int j=0;j<i;j++){ scrollEndX = scrollEndX + this.titleScroll.get(j).getWidth() + ApplyUtils.dip2px(getContext(), 20); } Log.i("liyuanjinglyjfanxiang", "scrollStartX=" + String.valueOf(scrollStartX) + "----scrollEndX" + String.valueOf(scrollEndX)); slideview(scrollStartX, scrollEndX); } LinearLayout.LayoutParams imageParams = (LinearLayout.LayoutParams) this.iv_nav_indicator.getLayoutParams(); imageParams.width = this.titleScroll.get(i).getWidth() + ApplyUtils.dip2px(getContext(), 20); imageParams.height = 5; this.iv_nav_indicator.setLayoutParams(imageParams); this.titleScroll.get(i).setTextColor(Color.RED); this.mLastPosition = position;//设置当前的标题索引 } else { this.titleScroll.get(i).setTextColor(Color.BLACK); } } }
这个代码的构思在本文第一节已经讲清楚了,这里不在赘述。
其代码在adapter包中。
没在书上见过吧!呵呵,这是书上没有的,技术嘛,还是跟着文档走。不过,好书也是有讲到的,只是很少。
其布局全是Fragment文件。开发网易新闻APP到现在你发现没有,除了一个Activity,我这里没用到第二个Activity,全部都是Fragment。这样对系统来说,负担要小的多。不过,后续还是会有第二个Activity,也就是新闻详情页面会用到一个,也仅仅就两个而已。
代码简单如PagerAdapter。其就是PagerAdapter的替代品。谷歌推荐你使用这个,只是换个名字而已。用法一模一样。
代码如下:
public class MyPagerAdapter extends FragmentPagerAdapter { public List<Fragment> fragment; public MyPagerAdapter(FragmentManager fm) { super(fm); } public MyPagerAdapter(FragmentManager fm, List<Fragment> fragments) { super(fm); this.fragment = fragments; } @Override public int getCount() { return fragment.size(); } @Override public Fragment getItem(int position) { return fragment.get(position); } }
我们知道,我们的横向标题是动态添加的,也可以无限增长,那么如果每个标题固定写一个Fragment那么有100个,那还不得累死我们这些程序员吗?
网络爬虫的时候就讲到过,其每个新闻页面都大同小异,除了获取的新闻网址不同外,其他的基本都是相同的,那么只要在动态创建Fragment的时候传进去该页面获取新闻的网址,那么就可以实现动态无限横向标题栏了。
我们用到的Fragment的代码如下,仅仅在构造函数中添加一个网址参数,其Fragment在fragment包中:
public class NewsViewPagerFragment extends Fragment{ private String urlStr; public NewsViewPagerFragment() { } public NewsViewPagerFragment(String url) { this.urlStr=url; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view=inflater.inflate(R.layout.news_viewpager_fragment_main,container,false); TextView urlTxt=(TextView)view.findViewById(R.id.news_viewpager_fragment_main_urltxt); urlTxt.setText(this.urlStr); return view; } }
因为整个布局都是在NewsFragment中显示的,所以我们初始化ViewPager的事情也在NewsFragment中。
代码如下:
/*** * 初始化ViewPager */ private void initViewPager(){ this.fragmentList=new ArrayList<>(); fragmentList.add(new NewsViewPagerFragment("http://news.163.com/mobile/")); fragmentList.add(new NewsViewPagerFragment("http://news.163.com/mobile/")); fragmentList.add(new NewsViewPagerFragment("http://3g.163.com/ntes/special/00340D52/3gtouchlist.html?docid=A9O2HAB6jiying&title=%E8%BD%BB%E6%9D%BE%E4%B8%80%E5%88%BB")); fragmentList.add(new NewsViewPagerFragment("http://3g.163.com/touch/money/")); fragmentList.add(new NewsViewPagerFragment("http://3g.163.com/touch/tech/")); pagerAdapter=new MyPagerAdapter(getActivity().getSupportFragmentManager(), fragmentList); this.viewPager.setAdapter(pagerAdapter);//设置adapter this.viewPager.setCurrentItem(0);//设置当前显示的页面 this.viewPager.addOnPageChangeListener(new OnPagerChangeListener());//设置监听函数 }
每个Fragment都是动态创建的,每个ViewPager的item都是一个新闻网址。所以找到网易手机访问首页的该标签的网址,添加到参数中创建Fragment。
下面我们来看看ViewPager的监听函数:
private class OnPagerChangeListener implements ViewPager.OnPageChangeListener{ //滑动中执行 @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } //滑动到某个页面执行 @Override public void onPageSelected(int position) { titleScroll.setPagerChangeListenerToTextView(position);//联动HorizontalScrollView} //状态改变时候调用 @Override public void onPageScrollStateChanged(int state) { } }
我们不仅让ViewPager改变的时候HorizontalScrollView有所改变,我们也要让HorizontalScrollView改变的时候ViewPager也要有所改变。
我们看看NewsFragment布局文件中关于ViewPager与HorizontalScrollView的布局是怎么写的,代码如下:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <include layout="@layout/lyj_title_bar_layout"/> <include layout="@layout/important_news_layout"/> <include layout="@layout/weather_layout"/> <LinearLayout android:id="@+id/news_fragment_main_horizontalscrollview" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/lyj_title_bar_layout_main" android:orientation="horizontal"> <com.example.liyuanjing.myview.NewsTitleHorizontalScrollView android:id="@+id/myHorizeontal" android:layout_width="0dp" android:layout_height="30dp" android:fillViewport="true" android:layout_below="@+id/news_activity_main_title_down" android:layout_weight="5" android:scrollbars="none"> </com.example.liyuanjing.myview.NewsTitleHorizontalScrollView> <LinearLayout android:id="@+id/news_fragment_main_layout_addtitle" android:layout_width="0dp" android:layout_height="28dp" android:layout_weight="1" android:clickable="true" android:gravity="center" android:orientation="vertical"> <ImageButton android:layout_width="20dp" android:layout_height="11dp" android:scaleType="fitXY" android:clickable="false" android:duplicateParentState="true" android:background="@drawable/biz_news_column_edit_arrow_down"/> </LinearLayout> </LinearLayout> <android.support.v4.view.ViewPager android:id="@+id/vPager" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/news_fragment_main_horizontalscrollview" android:layout_gravity="center" android:layout_weight="1.0" android:flipInterval="30" android:persistentDrawingCache="animation" /> </RelativeLayout>
大多数布局文件代码是不需要解释的,不过有几点还是要说明一下:
①android:scrollbars="none"默认HorizontalScrollView是有下划线的,就像ScrollView一样。一个有粗有长的横向滑动条。
②横向滑动菜单旁边有个箭头向下的按钮,其被包裹在LinearLayout中,该ImageButton的点击事件由其父Linearlayout拦截,并处理,这里我们没有实现其界面但点击这个按钮,默认会添加一个标题在横向滑动菜单中。当然这不是永久的,等把优化ListView加载新闻讲解后,就会来完善这个箭头界面和永久添加菜单。
③android:persistentDrawingCache="animation":定义绘图的高速缓存的持久性。 绘图缓存可能由一个ViewGroup在特定情况下为其所有的子类启用,例如在一个滚动的过程中。 此属性可以保留在内存中的缓存后其初始的使用。 坚持缓存会消耗更多的内存,但可能会阻止频繁的垃圾回收是反复创建缓存。 默认情况下持续存在设置为滚动。这里表示在布局动画之后,该绘图缓存一直保持。
④android:flipInterval="30":视图间切换的时间间隔。其他的属性前面都提到过,这里不在过多的解释。
所以NewsFragment中代码如下:
//初始化viewPager代码段 this.titleScroll=(NewsTitleHorizontalScrollView)view.findViewById(R.id.myHorizeontal); this.viewPager=(ViewPager)view.findViewById(R.id.vPager); this.addTitleLayout=(LinearLayout)view.findViewById(R.id.news_fragment_main_layout_addtitle); initViewPager(); //添加标题 this.addTitleLayout.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { titleScroll.addTextViewTitle("美女", getActivity()); fragmentList.add(new NewsViewPagerFragment("http://news.163.com/mobile/")); pagerAdapter = new MyPagerAdapter(getActivity().getSupportFragmentManager(), fragmentList); viewPager.setAdapter(pagerAdapter); pagerAdapter.notifyDataSetChanged(); new Handler().post(new Runnable() { public void run() { try { Thread.sleep(10); viewPager.setCurrentItem(fragmentList.size() - 1);//定位到最后一个ViewPager的item,并滑动菜单到最后一个刚添加的美女标题 } catch (Exception e) { e.printStackTrace(); } } }); } }); //实现监听 this.titleScroll.setOnItemClickListener(new NewsTitleHorizontalScrollView.OnItemClickListener() { @Override public void onClick(int pos) { viewPager.setCurrentItem(pos);//设置标题栏TextView的点击事件 } });
这里默认可以添加标题,不过都是美女标题,这里也用到了handle.post方法。同样停顿的10毫秒,也是为了等待界面的刷新,不然下划线获取不到刚刚添加的控件的宽度。
前面细分的讲解了HorizontalScrollView的实现,这里完整的看一下代码,首先HorizontalScrollView动态的添加了布局文件,其布局文件news_title_horizontalscrollview_layout.xml代码如下:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical"> <LinearLayout android:id="@+id/news_title_horizontalscrollview_titletxt_layout" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:id="@+id/ttTxt" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:clickable="true" android:textColor="@android:color/black" android:text="@string/news_title_ttTxt" android:textSize="20dp" /> <TextView android:id="@+id/ycTxt" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:clickable="true" android:textColor="@android:color/black" android:text="@string/news_title_ycTxt" android:textSize="20dp" /> <TextView android:id="@+id/qsykTxt" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:clickable="true" android:textColor="@android:color/black" android:text="@string/news_title_qsycTxt" android:textSize="20dp" /> <TextView android:id="@+id/caijinTxt" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:clickable="true" android:textColor="@android:color/black" android:text="@string/news_title_caijinTxt" android:textSize="20dp" /> <TextView android:id="@+id/kejiTxt" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:clickable="true" android:textColor="@android:color/black" android:text="@string/news_title_kejiTxt" android:textSize="20dp" /> </LinearLayout> <ImageView android:id="@+id/iv_nav_indicator" android:layout_width="1dip" android:layout_height="5dip" android:gravity="center" android:background="#FF0000" android:scaleType="matrix" /> </LinearLayout>
一个标题栏,一个下划线,标题栏里面有五个TextView。
最后亮出一下完整的NewsTitleHorizontalScrollView代码如下:
public class NewsTitleHorizontalScrollView extends HorizontalScrollView implements View.OnClickListener{ private LayoutInflater inflater;//加载布局进来用的 private View view;//布局View private LinearLayout titleAllTxt;//标题布局 private List<TextView> titleScroll = new ArrayList<>();//标题数组 private ImageView iv_nav_indicator;//下划线 private static int mLastPosition = 0;//当前选中标题索引 private OnItemClickListener mOnClickListener;//监听函数 @Override public void onClick(View v) { mOnClickListener.onClick(titleAllTxt.indexOfChild(v)); } public interface OnItemClickListener { void onClick(int pos); } public void setOnItemClickListener(OnItemClickListener mOnClickListener) { this.mOnClickListener = mOnClickListener; } public NewsTitleHorizontalScrollView(Context context, AttributeSet attrs) { super(context, attrs); initView(); } /** * 初始化加载进来的布局 */ private void initView() { this.inflater = LayoutInflater.from(getContext()); view = this.inflater.inflate(R.layout.news_title_horizontalscrollview_layout, null); this.titleAllTxt = (LinearLayout) view.findViewById(R.id.news_title_horizontalscrollview_titletxt_layout); for (int i = 0; i < this.titleAllTxt.getChildCount(); i++) { this.titleScroll.add((TextView) this.titleAllTxt.getChildAt(i)); ((TextView) this.titleAllTxt.getChildAt(i)).setOnClickListener(this); } this.iv_nav_indicator = (ImageView) view.findViewById(R.id.iv_nav_indicator); int w = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); int h = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); this.titleScroll.get(0).measure(w, h); LinearLayout.LayoutParams imageParams = (LinearLayout.LayoutParams) this.iv_nav_indicator.getLayoutParams(); imageParams.width = this.titleScroll.get(0).getMeasuredWidth() + 20; imageParams.height = 5; this.iv_nav_indicator.setLayoutParams(imageParams); addView(view); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); } /** * viewPager跳转之下划线联动方法 * @param position 跳转后的索引 */ public void setPagerChangeListenerToTextView(int position) { int scrollStartX = 0;//动画其实位置 int scrollEndX = 0;//动画结束位置 for (int i = 0; i < this.titleScroll.size(); i++) {//循环是为了设置其他标题为黑色 if (i == position) { if (mLastPosition < i) {//判断滑动方向,里面为左向右 for (int j = 0; j < mLastPosition; j++) { scrollStartX += this.titleScroll.get(j).getWidth() + ApplyUtils.dip2px(getContext(), 20); } for (int j=0;j<i;j++){ scrollEndX=scrollEndX + this.titleScroll.get(j).getWidth() + ApplyUtils.dip2px(getContext(), 20); } Log.i("liyuanjinglyj", "scrollStartX=" + String.valueOf(scrollStartX) + "----scrollEndX" + String.valueOf(scrollEndX)); slideview(scrollStartX, scrollEndX); } else {//判断滑动方向,里面为右向左 for (int j = 0; j <mLastPosition; j++) { scrollStartX += this.titleScroll.get(j).getWidth() + ApplyUtils.dip2px(getContext(), 20); } for (int j=0;j<i;j++){ scrollEndX = scrollEndX + this.titleScroll.get(j).getWidth() + ApplyUtils.dip2px(getContext(), 20); } Log.i("liyuanjinglyjfanxiang", "scrollStartX=" + String.valueOf(scrollStartX) + "----scrollEndX" + String.valueOf(scrollEndX)); slideview(scrollStartX, scrollEndX); } LinearLayout.LayoutParams imageParams = (LinearLayout.LayoutParams) this.iv_nav_indicator.getLayoutParams(); imageParams.width = this.titleScroll.get(i).getWidth() + ApplyUtils.dip2px(getContext(), 20); imageParams.height = 5; this.iv_nav_indicator.setLayoutParams(imageParams); this.titleScroll.get(i).setTextColor(Color.RED); this.mLastPosition = position;//设置当前的标题索引 } else { this.titleScroll.get(i).setTextColor(Color.BLACK); } } } /** * 滑动动画 * @param p1 起始 * @param p2 终点 */ public void slideview(float p1, float p2) { TranslateAnimation animation = new TranslateAnimation(p1, p2, 0, 0); animation.setInterpolator(new OvershootInterpolator()); animation.setDuration(300); animation.setFillEnabled(true); animation.setFillAfter(true); iv_nav_indicator.startAnimation(animation); } /** * 动态添加标题 * @param text 标题文字 * @param context 上下文 */ public void addTextViewTitle(String text, Context context) { TextView textView = new TextView(context); textView.setText(text); textView.setTextColor(Color.BLACK); textView.setClickable(true); textView.setTextSize(20.0f); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); params.setMargins(10, 0, 10, 0); textView.setLayoutParams(params); textView.setOnClickListener(this); this.titleAllTxt.addView(textView); this.titleScroll.add(textView); new Handler().post(new Runnable() { public void run() { try { Thread.sleep(10); int left = titleAllTxt.getMeasuredWidth() - getWidth(); if (left < 0) { left = 0; } scrollTo(left, 0); } catch (Exception e) { e.printStackTrace(); } } }); } }
横向滑动菜单的功能我们就全部完成了,欢迎下载源码:
http://download.csdn.net/detail/liyuanjinglyj/9241387
其还有功能有待后续扩展,如滑动菜单怎么在手指滑动中就开始滑动下划线,并保持联动。添加标题永久化。超过界面标题自动跳转到屏幕中显示标题等。后续在实现下拉箭头添加菜单的时候会讲,你也可以自己试试。毕竟程序员是练出来的,不是看出来的。
离开两到三个星期后,接载新闻数据库设计及优化ListView等。
添加菜单后的运行界面: