自从Gallery被谷歌废弃以后,Google推荐使用ViewPager和HorizontalScrollView来实现Gallery的效果。的确HorizontalScrollView可以实现Gallery的效果。

其实HorizontalScrollView与ScrollView使用的模式一样,里面的内容必须包含在一个布局里面比如LinearLayout等,不能在其标签内直接放入TextView等子控件,必须被包裹。

那么我们上图看看我们最终要实现的效果图:

     

我们在单个viewPager显示页面放入要获取的新闻网址用于区分功能实现效果。其参数(新闻网址)是创建的时候就传进入进去的,避免写死后,不能增加横向滑动菜单数,看看网易旁边的按钮。如下图:

这样方便我们扩展新闻,毕竟你不可能,也不应该对每一个横向标题固定创建一个Fragment那将是耗时且低效率的,我们选择动态增加。好了,我们开始实现我们应该实现的功能吧。

1.思路构思

编程中代码都是次要的,思路才是最重要的,没有思路,俗称码农。先思路后代码才能称为程序员。

看着这个横向滑动菜单,你想到了什么呢?会用到哪些技术?

首先我们可以看到,要想实现这个无限添加的横向滑动的菜单,和怎么都不变的下划线滑动效果,这里最重要的就是算法了。现在默认只有五个菜单,如果你用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);

2.自定义HorizontalScrollView

怎么设计我们的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);
        }}
}

这个代码的构思在本文第一节已经讲清楚了,这里不在赘述。

3.ViewPager的FragmentPagerAdapter

其代码在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;
    }
}

4.初始化ViewPager

因为整个布局都是在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&amp;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) {}
}

5.HorizontalScrollView设置回调监听

我们不仅让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毫秒,也是为了等待界面的刷新,不然下划线获取不到刚刚添加的控件的宽度。

6.自定义HorizontalScrollView

前面细分的讲解了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等。

添加菜单后的运行界面:

仿网易新闻APP(五)——无限横向滑动菜单(自定义HorizontalScrollView+ViewPager)相关推荐

  1. 仿网易新闻APP(三)——标题栏之24小时要闻

    感冒了几天,今天总算正常了点,想了想我还有未完成的最后一类博文,马上就跳起来,接着奋斗了.废话就不多说了先上图看看网易新闻APP的标题栏24小时要闻如下: 我们即将完成的效果图: 图片显示0是默认图片 ...

  2. 仿网易新闻APP(四)——标题栏之本市天气(百度定位与车联网之天气查询)

    废话不多说,上图: 下面用到的知识有,百度定位及车联网API的使用,当然车联网API看起来高大上,其实我们这里只用来获取车联网中的天气查询功能.其他的功能还有渐变动画及缩放动画,以及定时更新天气及定位 ...

  3. 【快速开发App实战】BUI高仿网易新闻App系列一、搭建App开发环境和工作空间

    一. 搭建App开发环境和工作空间 前言 我们的目标是要做一个真实的案例, 着重通过BUI框架及其相关工具的使用, 结合原生打包平台, 帮助大家理解一个App的开发过程. 以最新网易新闻的App为例, ...

  4. android 仿网易标题栏,仿网易新闻可滑动标题栏TabLayout(文字或图标)

    近期有需要,要做一个类似于网易新闻首页中的可滑动标题栏 TabLayout,根据大神写的 FlycoTabLayout 改造了一下,可以加载网络图片,主要实现内容如下: 1. 可配置标题选中效果(下划 ...

  5. Android 开源框架ViewPageIndicator 和 ViewPager 仿网易新闻客户端Tab标签

     转载请注明出处:http://blog.csdn.net/xiaanming/article/details/10766053 之前用JakeWharton的开源框架ActionBarSherl ...

  6. 仿网易新闻的页面(ViewPager作为RecyclerView的Header)

    需求 > 想实现一个仿网易新闻的页面,上面是轮播的图片,下面是 RecyclerView 显示新闻列表. 本文链接 http://blog.csdn.net/never_cxb/article/ ...

  7. 网易新闻 html5,HTML5+SWIPER仿网易新闻横滑翻页及联动

    [实例简介] 一套仿网易新闻的闻横滑翻页,联动导航,用到了swiper.js zepto.js scroll.js效果不错 [实例截图] [核心代码] swiper └── swiper ├── cs ...

  8. Android高仿网易新闻客户端之动态添加标签

    承接上一篇文章:Android高仿网易新闻客户端之首页,今天来实现动态添加标签效果. 动态标签页是一个流式布局,实现了宽度自动换行高度自动分配的功能,代码如下: FlowLayout.java pac ...

  9. android 仿网易标签切换,Android 仿网易新闻客户端Tab标签

    Android 开源框架ViewPageIndicator和ViewPager仿网易新闻客户端Tab标签 http://blog.csdn.net/xiaanming/article/details/ ...

最新文章

  1. Python元组字典
  2. C#实现有向无环图(DAG)拓扑排序
  3. UITableView全面解析
  4. 2、Reactive Extensions for .NET(译)
  5. spring系统学习:day4--Spring配置: 集合类型属性的注入
  6. 微信公众号自动回复 node
  7. 员外带你读论文:LINE: Large-scale Information Network Embedding
  8. java ee me se_java EE ME SE有什么关系
  9. spring项目获取ServletContext
  10. Scrum立会报告+燃尽图(十月二十四日总第十五次)
  11. vue ----组件数据共享
  12. python决策树分类案例_银行产品销售案例与决策树分类算法
  13. 玩转python字典与列表(下)
  14. oppoa1计算机记录删了怎么办,捡到oppoA1怎么解锁
  15. ros自带到期通知_iPhone自带的8个App,没想到这么好用!
  16. 前端学习从入门到高级全程记录之43 (PHP基础Ⅵ)
  17. c++复习——侯捷版
  18. 【物理应用】Matlab实现两端固支梁热力耦合的有限元分析
  19. getch()和getche()
  20. 【前端】js轮播图,简洁代码,一目了然

热门文章

  1. 新南威语言班C加,11年澳大利亚新南威尔士大学语言班分为两类型
  2. 成为架构师,需要这些能力
  3. SVN代码管理协同开发流程
  4. C语言中voliate关键字的作用
  5. Lepton 无损压缩原理及性能分析
  6. Android大量项目源码
  7. matlab脚本编程习题
  8. 双加热纸塑分离机 分离牛皮纸袋怎么个两种方法
  9. 寒假python培训
  10. 用break还是continue?