仿今日头条顶部导航效果

 之前发现很多人在群里面、论坛上求网易新闻客户端的源码,之后我就去下了个网易新闻客户端和今日头条新闻客户端,发现他们的大体是一样的,于是在最近的空闲时间,便去琢磨如何去实现这样一个APP。

要知道它们是如何实现的,用到了什么第三方库文件,反编译便是很好的一个了解方法,如果你想要了解如何反编译可以点击这个链接:反编译就这么简单

只是一般的APK打包后都是被混淆过的,所以没那么好了解他的每个界面是如何实现的,没事,那就自己慢慢摸索或则从它的资源文件中提取布局了解下整体的大概情况。

我通过反编译 --今日头条:

知道了它用到的架包有,提取了有用的部分:

1.android-support-v4.jar (最常用的官方架包之一)

2.android-support-v7.jar (主要用于ActionBar的低版本兼容)

3.handmark.pulltorefresh.library  (图片的下拉刷新包)

4.slidingmenu.lib  (侧拉菜单包)   使用方法配置以及下载:点击这里

5.umeng (友盟的官方架包)

自己要在加用上的架包有:

1.Android-Universal-Image-Loader  (图片的异步加载包)   使用方法配置以及下载

注:发现架包中有aaa什么的命名,说明它被混淆过,所以要想进一步获取它的源码很困难,只能按照自己的思路去走。

好的,大体了解了它的整体结构,下面就开始它是如何开发的把:

注:本代码内用到的资源文件和属性配置部分从APK反编译的资源(SRC文件)中提取,为了达到更好的实现效果。

一.首先构建大体的框架,架包等用到的时候在导入

命名规范可以参考:android命名规范

二.进行配置

首先去掉应用的title栏目:

采取修改AndroidManifest.xml文件中application的android:theme="@style/AppTheme"属性:

<style name="AppTheme" parent="AppBaseTheme">  
    <item name="android:windowNoTitle">true</item>  
</style>  

三.开始开发

设置欢迎界面的调整动画,2秒

start_anima = new AlphaAnimation(0.3f, 1.0f);  
start_anima.setDuration(2000);  
view.startAnimation(start_anima);  
start_anima.setAnimationListener(new AnimationListener() {  
 
 @Override 
 public void onAnimationStart(Animation animation) {  
 // TODO Auto-generated method stub 
 
    }  
 
 @Override 
 public void onAnimationRepeat(Animation animation) {  
 // TODO Auto-generated method stub 
 
    }  
 
 @Override 
 public void onAnimationEnd(Animation animation) {  
 // TODO Auto-generated method stub 
        redirectTo();//跳转 
    }  
});  

之后便是主界面:

可以发现主界面上方的栏目栏是可以横向拖动的,并且选择。

下面就首先来实现上部栏目拖动这个效果:

大体思路结构图:

整体的布局文件是如下这样:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:tools="http://schemas.android.com/tools" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:orientation="vertical" >  
 
    <include layout="@layout/main_head" />  
 
    <LinearLayout  
        android:layout_width="match_parent" 
        android:layout_height="40.0dip" 
        android:background="#fff3f3f3" 
        android:orientation="horizontal" >  
 
        <RelativeLayout  
            android:id="@+id/rl_column" 
            android:layout_width="match_parent" 
            android:layout_height="40.0dip" 
            android:layout_weight="1.0" >  
 
            <com.topnews.view.ColumnHorizontalScrollView  
                android:id="@+id/mColumnHorizontalScrollView" 
                android:layout_width="match_parent" 
                android:layout_height="40.0dip" 
                android:scrollbars="none" >  
 
                <LinearLayout  
                    android:id="@+id/mRadioGroup_content" 
                    android:layout_width="fill_parent" 
                    android:layout_height="40.0dip" 
                    android:layout_centerVertical="true" 
                    android:gravity="center_vertical" 
                    android:orientation="horizontal" 
                    android:paddingLeft="10.0dip" 
                    android:paddingRight="10.0dip" />  
            </com.topnews.view.ColumnHorizontalScrollView>  
 
            <ImageView  
                android:id="@+id/shade_left" 
                android:layout_width="10.0dip" 
                android:layout_height="40.0dip" 
                android:layout_alignParentLeft="true" 
                android:layout_centerVertical="true" 
                android:background="@drawable/channel_leftblock" 
                android:visibility="gone" />  
 
            <ImageView  
                android:id="@+id/shade_right" 
                android:layout_width="10.0dip" 
                android:layout_height="40.0dip" 
                android:layout_alignParentRight="true" 
                android:layout_centerVertical="true" 
                android:background="@drawable/channel_rightblock" 
                android:visibility="visible" />  
        </RelativeLayout>  
 
        <LinearLayout  
            android:id="@+id/ll_more_columns" 
            android:layout_width="wrap_content" 
            android:layout_height="40.0dip" >  
 
            <ImageView  
                android:id="@+id/button_more_columns" 
                android:layout_width="40.0dip" 
                android:layout_height="40.0dip" 
                android:layout_gravity="center_vertical" 
                android:src="@drawable/channel_glide_day_bg" />  
        </LinearLayout>  
    </LinearLayout>  
 
    <View  
        android:id="@+id/category_line" 
        android:layout_width="fill_parent" 
        android:layout_height="0.5dip" 
        android:background="#ffdddddd" />  
 
    <android.support.v4.view.ViewPager  
        android:id="@+id/mViewPager" 
        android:layout_width="match_parent" 
        android:layout_height="match_parent" />  
 
</LinearLayout>  

由于发现HorizontalScrollView左右拖动的时候没有那种阴影效果,所以在这里,我们发现了头条的资源文件下有这么2个文件:

这个就是它在白天模式和黑夜模式下用的阴影图片。那我们可以采取重写HorizontalScrollView来判断滚动,如果滚动时候不是在最左边,显示左边阴影,不是在最右边,显示右边阴影。

public class ColumnHorizontalScrollView extends HorizontalScrollView {  
 /** 传入整体布局  */ 
 private View ll_content;  
 /** 传入更多栏目选择布局 */ 
 private View ll_more;  
 /** 传入拖动栏布局 */ 
 private View rl_column;  
 /** 左阴影图片 */ 
 private ImageView leftImage;  
 /** 右阴影图片 */ 
 private ImageView rightImage;  
 /** 屏幕宽度 */ 
 private int mScreenWitdh = 0;  
 /** 父类的活动activity */ 
 private Activity activity;  
 
 public ColumnHorizontalScrollView(Context context) {  
 super(context);  
    }  
 
 public ColumnHorizontalScrollView(Context context, AttributeSet attrs) {  
 super(context, attrs);  
    }  
 
 public ColumnHorizontalScrollView(Context context, AttributeSet attrs,  
 int defStyle) {  
 super(context, attrs, defStyle);  
    }  
 /**  
     * 在拖动的时候执行 
     * */ 
 @Override 
 protected void onScrollChanged(int paramInt1, int paramInt2, int paramInt3, int paramInt4) {  
 // TODO Auto-generated method stub 
 super.onScrollChanged(paramInt1, paramInt2, paramInt3, paramInt4);  
        shade_ShowOrHide();  
 if(!activity.isFinishing() && ll_content !=null && leftImage!=null && rightImage!=null && ll_more!=null && rl_column !=null){  
 if(ll_content.getWidth() <= mScreenWitdh){  
                leftImage.setVisibility(View.GONE);  
                rightImage.setVisibility(View.GONE);  
            }  
        }else{  
 return;  
        }  
 if(paramInt1 ==0){  
            leftImage.setVisibility(View.GONE);  
            rightImage.setVisibility(View.VISIBLE);  
 return;  
        }  
 if(ll_content.getWidth() - paramInt1 + ll_more.getWidth() + rl_column.getLeft() == mScreenWitdh){  
            leftImage.setVisibility(View.VISIBLE);  
            rightImage.setVisibility(View.GONE);  
 return;  
        }  
        leftImage.setVisibility(View.VISIBLE);  
       <span style="white-space:pre">   </span>rightImage.setVisibility(View.VISIBLE);  
    }  
 /**  
     * 传入父类布局中的资源文件 
     * */ 
 public void setParam(Activity activity, int mScreenWitdh,View paramView1,ImageView paramView2, ImageView paramView3 ,View paramView4,View paramView5){  
 this.activity = activity;  
 this.mScreenWitdh = mScreenWitdh;  
        ll_content = paramView1;  
        leftImage = paramView2;  
        rightImage = paramView3;  
        ll_more = paramView4;  
        rl_column = paramView5;  
    }  
 /**  
     * 判断左右阴影的显示隐藏效果 
     * */ 
 public void shade_ShowOrHide() {  
 if (!activity.isFinishing() && ll_content != null) {  
            measure(0, 0);  
 //如果整体宽度小于屏幕宽度的话,那左右阴影都隐藏 
 if (mScreenWitdh >= getMeasuredWidth()) {  
                leftImage.setVisibility(View.GONE);  
                rightImage.setVisibility(View.GONE);  
            }  
        } else {  
 return;  
        }  
 //如果滑动在最左边时候,左边阴影隐藏,右边显示 
 if (getLeft() == 0) {  
            leftImage.setVisibility(View.GONE);  
            rightImage.setVisibility(View.VISIBLE);  
 return;  
        }  
 //如果滑动在最右边时候,左边阴影显示,右边隐藏 
 if (getRight() == getMeasuredWidth() - mScreenWitdh) {  
            leftImage.setVisibility(View.VISIBLE);  
            rightImage.setVisibility(View.GONE);  
 return;  
        }  
 //否则,说明在中间位置,左、右阴影都显示 
        leftImage.setVisibility(View.VISIBLE);  
        rightImage.setVisibility(View.VISIBLE);  
    }  
}  

之后

private ArrayList<NewsClassify> newsClassify=new ArrayList<NewsClassify>();

根据newsClassify这个栏目分类列表里面的数量进行添加栏目。(这里首先采用了自己限定的ITEM,而没有进行数据库的操作,以后加上)

ViewPage的适配器NewsFragmentPagerAdapter,通过ViewPage切换对应栏目的的Fragment:

public class NewsFragmentPagerAdapter extends FragmentPagerAdapter {  
 private ArrayList<Fragment> fragments;  
 private FragmentManager fm;  
 
 public NewsFragmentPagerAdapter(FragmentManager fm) {  
 super(fm);  
 this.fm = fm;  
    }  
 
 public NewsFragmentPagerAdapter(FragmentManager fm,  
            ArrayList<Fragment> fragments) {  
 super(fm);  
 this.fm = fm;  
 this.fragments = fragments;  
    }  
 
 @Override 
 public int getCount() {  
 return fragments.size();  
    }  
 
 @Override 
 public Fragment getItem(int position) {  
 return fragments.get(position);  
    }  
 
 @Override 
 public int getItemPosition(Object object) {  
 return POSITION_NONE;  
    }  
 
 public void setFragments(ArrayList<Fragment> fragments) {  
 if (this.fragments != null) {  
            FragmentTransaction ft = fm.beginTransaction();  
 for (Fragment f : this.fragments) {  
                ft.remove(f);  
            }  
            ft.commit();  
            ft = null;  
            fm.executePendingTransactions();  
        }  
 this.fragments = fragments;  
        notifyDataSetChanged();  
    }  
 
 @Override 
 public Object instantiateItem(ViewGroup container, final int position) {  
        Object obj = super.instantiateItem(container, position);  
 return obj;  
    }  
 
}  

之后添加栏目ITEM:

int count =  newsClassify.size();  
 for(int i = 0; i< count; i++){  
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(mItemWidth , LayoutParams.WRAP_CONTENT);  
        params.leftMargin = 10;  
        params.rightMargin = 10;  
        TextView localTextView = new TextView(this);  
        localTextView.setTextAppearance(this, R.style.top_category_scroll_view_item_text);  
        localTextView.setBackgroundResource(R.drawable.radio_buttong_bg);  
        localTextView.setGravity(Gravity.CENTER);  
        localTextView.setPadding(5, 0, 5, 0);  
        localTextView.setId(i);  
        localTextView.setText(newsClassify.get(i).getTitle());  
        localTextView.setTextColor(getResources().getColorStateList(R.color.top_category_scroll_text_color_day));  
 if(columnSelectIndex == i){  
            localTextView.setSelected(true);  
        }  
        localTextView.setOnClickListener(new OnClickListener() {  
 
 @Override 
 public void onClick(View v) {  
 for(int i = 0;i < mRadioGroup_content.getChildCount();i++){  
                      View localView = mRadioGroup_content.getChildAt(i);  
 if (localView != v)  
                          localView.setSelected(false);  
 else{  
                          localView.setSelected(true);  
                          mViewPager.setCurrentItem(i);  
                      }  
                  }  
                  Toast.makeText(getApplicationContext(), newsClassify.get(v.getId()).getTitle(), Toast.LENGTH_SHORT).show();  
            }  
        });  
        mRadioGroup_content.addView(localTextView, i ,params);  
    }  

之后根据选择栏目的来调整ColumnHorizontalScrollView中的位置

<span style="white-space:pre">  </span>/**  
     *  选择的Column里面的Tab 
     * */ 
 private void selectTab(int tab_postion) {  
        columnSelectIndex = tab_postion;  
 for (int i = 0; i < mRadioGroup_content.getChildCount(); i++) {  
            View checkView = mRadioGroup_content.getChildAt(tab_postion);  
 int k = checkView.getMeasuredWidth();  
 int l = checkView.getLeft();  
 int i2 = l + k / 2 - mScreenWidth / 2;  
 // rg_nav_content.getParent()).smoothScrollTo(i2, 0); 
            mColumnHorizontalScrollView.smoothScrollTo(i2, 0);  
 // mColumnHorizontalScrollView.smoothScrollTo((position - 2) * 
 // mItemWidth , 0); 
        }  
 //判断是否选中 
 for (int j = 0; j <  mRadioGroup_content.getChildCount(); j++) {  
            View checkView = mRadioGroup_content.getChildAt(j);  
 boolean ischeck;  
 if (j == tab_postion) {  
                ischeck = true;  
            } else {  
                ischeck = false;  
            }  
            checkView.setSelected(ischeck);  
        }  
    }  

完成的效果如下:

更多注释和实现方法可以查看DEMO源码文件,源码下载地址 : DEMO源码

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏向治洪

gradeview可拖动效果实现

下面先上这次实现功能的效果图:(注:这个效果图没有拖拽的时候移动动画,DEMO里面有,可以下载看看) ? 一、开发心里历程 刚开始接触这个的时候,不知道要如何实...

3308
来自专栏developerHaoz 的安卓之旅

Android 打造一个丝滑的自动轮播控件

现在很多的 App 都有自动轮播的 banner 界面,用于展示广告图片或者显示当前比较热门的一些活动,除了具备比较酷炫的效果之外,通过轮播的方式来减少对界面的...

1172
来自专栏Android干货

安卓开发_慕课网_百度地图_添加覆盖物

31010
来自专栏郭霖

Android照片墙加强版,使用ViewPager实现画廊效果

记得关于照片墙的文章我已经写过好几篇了,有最基本的照片墙,有瀑布流模式的照片墙,后来又在瀑布流的基础之上加入了查看大图和多点触控缩放的功能。总体来说,照片墙这个...

3967
来自专栏Android先生

项目需求讨论-Android 自定义Dialog实现步骤及封装

在项目中,我们会遇到各种各样的界面需求,比如对话框和选择框,都是会配合具体项目的UI界面来做,而不是说用自带的弹出框。比如下面在登录界面的二个对话框效果。都是我...

2602
来自专栏Android干货园

Android 高仿微信群聊头像

版权声明:本文为博主原创文章,转载请标明出处。 https://blog.csdn.net/lyhhj/article/details/49...

2862
来自专栏刘望舒

Android 屏幕适配从未如此简单

一个月前看了今日头条新的屏幕适配方案,对此不禁拍案叫绝,为此我想把这种方案融入到我工具类中直接一行代码即可适配,如今最新 1.18.0 版 AndroidUti...

1242
来自专栏青玉伏案

Android开发之自定义的ListView(UITableViewController)

Android开发中的ListView, 顾名方法思义,就是表视图。表示图在iOS开发中就是TableView。两者虽然名称不一样,但是其使用方法,使用场景以及...

1938
来自专栏androidBlog

自定义 Behavior - 仿新浪微博发现页的实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gdutxiaoxu/article/de...

1072
来自专栏小鄧子的技术博客专栏

【译】Activity分割动画如何使用我的动画##

在切换不同Activity时,系统级过渡动画是作用于整个Activity的,而我想要实现的动画效果是将Activity A分割成两部分,然后将他们向外推开,最后...

1122

扫码关注云+社区

领取腾讯云代金券