一個小時打造新聞app
前言
作為一個新手,學完基礎總想做點什么東西出來。于是我試著去模仿那些優秀的開源作品。
模仿作品: LookLook開源項目
經過一些波折和學習,寫下模仿過程。
一個小時打造新聞app
實際上我花了大概三天才弄懂所有的東西,不過有了經驗確實可以在一個小時里完成。
使用框架
rxjava和retrofit以及一個開源擴展的recyclerview和注解框架butterknife
集體依賴如下:
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:24.2.1'
compile 'com.android.support:design:24.2.1'
testCompile 'junit:junit:4.12'
//依賴注解
//依賴添加
compile 'com.jakewharton:butterknife:8.4.0'
apt 'com.jakewharton:butterknife-compiler:8.4.0'
compile 'com.google.code.gson:gson:2.7'
//高級的recyclerview
compile 'com.jude:easyrecyclerview:4.2.3'
compile 'com.android.support:recyclerview-v7:24.2.0'
//rxjava
compile 'com.squareup.retrofit2:retrofit-converters:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'io.reactivex:rxandroid:1.2.1'
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-scalars:2.1.0'
compile 'com.github.bumptech.glide:glide:3.7.0'
}
開始制作app
界面制作
新建項目,選擇模板---->調整模板
菜單調整
可以看到有menu里面兩個文件
一個是主菜單,顯示在Toobar上面
另一個是抽屜的菜單,按需修改即可。
activity_main_drawer.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android=";
<group android:checkableBehavior="single">
<item
android:id="@+id/nav_camera"
android:icon="@drawable/ic_menu_slideshow"
android:title="新聞精選" />
<item
android:id="@+id/nav_gallery"
android:icon="@drawable/ic_face_black_24dp"
android:title="輕松一刻" />
<item
android:id="@+id/nav_slideshow"
android:icon="@drawable/ic_menu_gallery"
android:title="每日美圖" />
<item
android:id="@+id/nav_manage"
android:icon="@drawable/ic_menu_manage"
android:title="應用推薦" />
</group>
<item android:title="其他">
<menu>
<item
android:id="@+id/nav_share"
android:icon="@drawable/ic_menu_share"
android:title="軟件分享" />
<item
android:id="@+id/nav_send"
android:icon="@drawable/ic_menu_send"
android:title="軟件關于" />
</menu>
</item>
</menu></code></pre>
抽屜除了menu還有上面一部分,可以設置頭像和簽名。
nav_header_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="
<ImageView
android:layout_gravity="center"
android:id="@+id/imageView"
android:layout_width="100dp"
android:layout_height="100dp"
app:srcCompat="@drawable/ic_app_icon" />
<TextView
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="一日之計在于晨,一年之計在于春。"
android:textAppearance="@style/TextAppearance.AppCompat.Body1" />
<TextView
android:gravity="center"
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="1458476478@qq.com" />
</LinearLayout></code></pre>
主界面大概這就可以了,剩下的就是要動態添加fragement到FragLayout里面去。
數據獲取
首先新建fragment_news,布局文件只需要一個 EasyRecyclerView 即可
添加依賴
//高級的recyclerview
compile 'com.jude:easyrecyclerview:4.2.3'
compile 'com.android.support:recyclerview-v7:24.2.0'
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.jude.easyrecyclerview.EasyRecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:recyclerClipToPadding="true"
app:recyclerPadding="8dp"
app:recyclerPaddingBottom="8dp"
app:recyclerPaddingLeft="8dp"
app:recyclerPaddingRight="8dp"
app:recyclerPaddingTop="8dp"
app:scrollbarStyle="insideOverlay"
app:scrollbars="none" />
</LinearLayout>
使用rxjava和retrofit獲取json數據
我的數據來自 天性數據 ,只需要注冊即可獲得APIKEY。
數據請求核心代碼:
創建retrofit的請求接口
public interface ApiService{
@GET("social/")
Observable <NewsGson> getNewsData(@Query("key")String key,@Query("num") String num,@Query("page") int page);
注意返回的是Gson數據而且設置為"被觀察者"
數據獲取函數:
private void getData() {
Log.d("page", page + "");
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
Toast.makeText(getContext(),
"網絡連接失敗", Toast.LENGTH_LONG).show();
}
});
page = page + 1;
}</code></pre>
- 使用retrofit 發起網絡請求
- 數據通過rxjava提交先在io線程里,返回到主線程
- 中間設置map 轉換 把得到的Gson類轉化為所需的News類(可以省略這一步)
- subscribe的onNext里處理返回的最終數據。
關于建立Gson類
Gson是谷歌的Json處理包,添加依賴。
compile 'com.google.code.gson:gson:2.7'
配合插件:GsonFormat可以快速通過json數據建立對應類。

數據綁定到recyview
由于我們使用的是被擴展的recyview,所以用起來很方便。
具體使用去作者的githua EasyRecyclerView
-
Adapter
繼承recycle的adapter,主要返回自己的ViewHolder
public class NewsAdapter extends RecyclerArrayAdapter<News> {
public NewsAdapter(Context context) {
super(context);
}
@Override
public BaseViewHolder OnCreateViewHolder(ViewGroup parent, int viewType) {
return new NewsViewHolder(parent);
}
}</code></pre> </li>
- ViewHolder
</ol>
public class NewsViewHolder extends BaseViewHolder<News> {
private TextView mTv_name;
private ImageView mImg_face;
private TextView mTv_sign;
public NewsViewHolder(ViewGroup parent) {
super(parent,R.layout.news_recycler_item);
mTv_name = $(R.id.person_name);
mTv_sign = $(R.id.person_sign);
mImg_face = $(R.id.person_face); }
@Override
public void setData(final News data) {
mTv_name.setText(data.getTitle());
mTv_sign.setText(data.getCtime());
Glide.with(getContext())
.load(data.getPicUrl())
.placeholder(R.mipmap.ic_launcher)
.centerCrop()
.into(mImg_face);
}
}</code></pre>
3.設置recycleview
recyclerView.setAdapter(adapter = new NewsAdapter(getActivity()));
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
//添加邊框
SpaceDecoration itemDecoration = new SpaceDecoration((int) PixUtil.convertDpToPixel(8, getContext()));
itemDecoration.setPaddingEdgeSide(true);
itemDecoration.setPaddingStart(true);
itemDecoration.setPaddingHeaderFooter(false);
recyclerView.addItemDecoration(itemDecoration);
//更多加載
adapter.setMore(R.layout.view_more, new RecyclerArrayAdapter.OnMoreListener() {
@Override
public void onMoreShow() {
getData();
}
@Override
public void onMoreClick() {
}
});
//寫刷新事件
recyclerView.setRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
recyclerView.postDelayed(new Runnable() {
@Override
public void run() {
adapter.clear();
page = 0;
getData();
}
}, 1000);
}
});
//點擊事件
adapter.setOnItemClickListener(new RecyclerArrayAdapter.OnItemClickListener() {
@Override
public void onItemClick(int position) {
ArrayList<String> data = new ArrayList<String>();
data.add(adapter.getAllData().get(position).getPicUrl());
data.add(adapter.getAllData().get(position).getUrl());
Intent intent = new Intent(getActivity(), NewsDetailsActivity.class);
//用Bundle攜帶數據
Bundle bundle = new Bundle();
bundle.putStringArrayList("data", data);
intent.putExtras(bundle);
startActivity(intent);
}
});</code></pre>
Glide網絡圖片加載庫
一個專注于平滑圖片加載的庫:
依賴:
compile 'com.github.bumptech.glide:glide:3.7.0'
基本使用:
Glide.with(mContext)
.load(path)
.asGif()
.override(300,300)
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.placeholder(R.drawable.progressbar)
.thumbnail(1f)
.error(R.drawable.error)
.transform(new MyBitmapTransformation(mContext,10f))
.into(iv);
新聞詳情頁
布局:使用CoordinatorLayout實現上拉toolbar壓縮動畫。
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="256dp"
android:fitsSystemWindows="true"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleMarginEnd="64dp"
app:expandedTitleMarginStart="48dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
android:src="@mipmap/ic_launcher"
android:id="@+id/ivImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:scaleType="centerCrop"
android:transitionName="新聞圖片"
app:layout_collapseMode="parallax"
app:layout_collapseParallaxMultiplier="0.7" />
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<WebView
android:id="@+id/web_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"></WebView>
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout></code></pre>
CoordinatorLayout+AppBarLayout里面配合CollapsingToolbarLayout布局技能實現toolbar的動畫:

上面的Imgview加載圖片,下面的webview加載文章內容
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_news_detail);
toolbar.setTitle("新聞詳情");
setSupportActionBar(toolbar);
// 設置返回箭頭
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
onBackPressed();
}
});
//新頁面接收數據
Bundle bundle = this.getIntent().getExtras();
//接收name值
final ArrayList<String> data = bundle.getStringArrayList("data");
Log.d("url", data.get(0));
webText.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// TODO Auto-generated method stub
view.loadUrl(url);
return true;
}
});
webText.loadUrl(data.get(1));
Glide.with(this)
.load(data.get(0)).error(R.mipmap.ic_launcher)
.fitCenter().into(ivImage);
}</code></pre>
到這里基本完成:最后動態添加fragment
//菜單事件添加
if (id == R.id.nav_camera) {
// Handle the camera action
NewsFragment fragment=new NewsFragment();
FragmentManager fragmentManager=getSupportFragmentManager();
FragmentTransaction transaction=fragmentManager.beginTransaction();
transaction.replace(R.id.fragment_container,fragment);
transaction.commit();
}
效果測試:

my.gif

總結:
只是勉強能用,還有很多細節沒有優化。接下來好要繼續學習。
補充:關于ButterKnife的使用
框架導入:
搜索依賴butterknife導入:
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:24.2.0'
//依賴添加
compile 'com.jakewharton:butterknife:8.4.0'
}
使用步驟:
注意我這里寫的是8.40版本,和以前的有區別。
如果的ButterKnife是8.01或者以上的話
需要添加以下內容:
1. classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.2'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
2. apply plugin: 'com.neenbedankt.android-apt'
apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt'
3. apt 'com.jakewharton:butterknife-compiler:8.4.0'
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:24.2.0'
//依賴添加
compile 'com.jakewharton:butterknife:8.4.0'
apt 'com.jakewharton:butterknife-compiler:8.4.0'
}
Android Studio上方便使用butterknife注解框架的偷懶插件 Android Butterknife Zelezny :

技巧:鼠標要移動到布局文件名上。