(android高仿系列)今日头条 --新闻阅读器 (一)

在模仿中循序渐进,以程序员角度去看待每一个APP是如何实现的,它有什么优缺点,并从中提升自己。

       之前发现很多人在群里面、论坛上求网易新闻客户端的源码,之后我就去下了个网易新闻客户端和今日头条新闻客户端,发现他们的大体是一样的,于是在最近的空闲时间,便去琢磨如何去实现这样一个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);
	   	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中的位置
	/** 
	 *  选择的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源码


第2篇已经优化完成点击以下链接:

(android高仿系列)今日头条 --新闻阅读器 (二)

已标记关键词 清除标记
相关推荐
<p> <b><span style="background-color:#FFE500;">【超实用课程内容】</span></b> </p> <p> <br /> </p> <p> <br /> </p> <p> 本课程内容包含讲解<span>解读Nginx的基础知识,</span><span>解读Nginx的核心知识、带领学员进行</span>高并发环境下的Nginx性能优化实战,让学生能够快速将所学融合到企业应用中。 </p> <p> <br /> </p> <p style="font-family:Helvetica;color:#3A4151;font-size:14px;background-color:#FFFFFF;"> <b><br /> </b> </p> <p style="font-family:Helvetica;color:#3A4151;font-size:14px;background-color:#FFFFFF;"> <b><span style="background-color:#FFE500;">【课程如何观看?】</span></b> </p> <p style="font-family:Helvetica;color:#3A4151;font-size:14px;background-color:#FFFFFF;"> PC端:<a href="https://edu.csdn.net/course/detail/26277"><span id="__kindeditor_bookmark_start_21__"></span></a><a href="https://edu.csdn.net/course/detail/27216">https://edu.csdn.net/course/detail/27216</a> </p> <p style="font-family:Helvetica;color:#3A4151;font-size:14px;background-color:#FFFFFF;"> 移动端:CSDN 学院APP(注意不是CSDN APP哦) </p> <p style="font-family:Helvetica;color:#3A4151;font-size:14px;background-color:#FFFFFF;"> 本课程为录播课,课程永久有效观看时长,大家可以抓紧时间学习后一起讨论哦~ </p> <p style="font-family:"color:#3A4151;font-size:14px;background-color:#FFFFFF;"> <br /> </p> <p class="ql-long-24357476" style="font-family:"color:#3A4151;font-size:14px;background-color:#FFFFFF;"> <strong><span style="background-color:#FFE500;">【学员专享增值服务】</span></strong> </p> <p class="ql-long-24357476" style="font-family:"color:#3A4151;font-size:14px;background-color:#FFFFFF;"> <b>源码开放</b> </p> <p class="ql-long-24357476" style="font-family:"color:#3A4151;font-size:14px;background-color:#FFFFFF;"> 课件、课程案例代码完全开放给你,你可以根据所学知识,自行修改、优化 </p> <p class="ql-long-24357476" style="font-family:"color:#3A4151;font-size:14px;background-color:#FFFFFF;"> 下载方式:电脑登录<a href="https://edu.csdn.net/course/detail/26277"></a><a href="https://edu.csdn.net/course/detail/27216">https://edu.csdn.net/course/detail/27216</a>,播放页面右侧点击课件进行资料打包下载 </p> <p> <br /> </p> <p> <br /> </p> <p> <br /> </p>
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页