之前發現很多人在群裡面、論壇上求網易新聞用戶端的源碼,之後我就去下了個網易新聞用戶端和今日頭條新聞用戶端,發現他們的大體是一樣的,于是在最近的空閑時間,便去琢磨如何去實作這樣一個app。
隻是一般的apk打包後都是被混淆過的,是以沒那麼好了解他的每個界面是如何實作的,沒事,那就自己慢慢摸索或則從它的資源檔案中提取布局了解下整體的大概情況。
我通過反編譯 --今日頭條:
知道了它用到的架包有,提取了有用的部分:
1.android-support-v4.jar (最常用的官方架包之一)
2.android-support-v7.jar (主要用于actionbar的低版本相容)
3.handmark.pulltorefresh.library (圖檔的下拉重新整理包)
4.slidingmenu.lib (側拉菜單包)
5.umeng (友盟的官方架包)
自己要在加用上的架包有:
注:發現架包中有aaa什麼的命名,說明它被混淆過,是以要想進一步擷取它的源碼很困難,隻能按照自己的思路去走。
好的,大體了解了它的整體結構,下面就開始它是如何開發的把:
注:本代碼内用到的資源檔案和屬性配置部分從apk反編譯的資源(src檔案)中提取,為了達到更好的實作效果。
一.首先建構大體的架構,架包等用到的時候在導入
二.進行配置
首先去掉應用的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
}
public void onanimationrepeat(animation animation) {
public void onanimationend(animation animation) {
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_alignparentleft="true"
android:layout_centervertical="true"
android:background="@drawable/channel_leftblock"
android:visibility="gone" />
android:id="@+id/shade_right"
android:layout_alignparentright="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" >
android:id="@+id/button_more_columns"
android:layout_width="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_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);
/**
* 在拖動的時候執行
* */
protected void onscrollchanged(int paramint1, int paramint2, int paramint3, int paramint4) {
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);
if(ll_content.getwidth() - paramint1 + ll_more.getwidth() + rl_column.getleft() == mscreenwitdh){
leftimage.setvisibility(view.visible);
rightimage.setvisibility(view.gone);
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()) {
} else {
//如果滑動在最左邊時候,左邊陰影隐藏,右邊顯示
if (getleft() == 0) {
//如果滑動在最右邊時候,左邊陰影顯示,右邊隐藏
if (getright() == getmeasuredwidth() - mscreenwitdh) {
//否則,說明在中間位置,左、右陰影都顯示
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) {
this.fragments = fragments;
public int getcount() {
return fragments.size();
public fragment getitem(int position) {
return fragments.get(position);
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();
notifydatasetchanged();
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源碼檔案,源碼下載下傳位址 :