一个小时打造新闻app,打造新闻app,于是我试着去模仿那些优秀
一个小时打造新闻app,打造新闻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
界面制作:
新建项目,选择模板---->调整模板
QQ截图20161025085115.png
菜单调整
可以看到有menu里面两个文件
一个是主菜单,显示在Toobar上面
另一个是抽屉的菜单,按需修改即可。activity_main_drawer.xml
<?xml version="1.0" encoding="utf-8"?><menu xmlns:android="http://schemas.android.com/apk/res/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>
抽屉除了menu还有上面一部分,可以设置头像和签名。nav_header_main.xml
<?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="@dimen/nav_header_height" android:background="@drawable/side_nav_bar" android:gravity="bottom" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:theme="@style/ThemeOverlay.AppCompat.Dark" android:orientation="vertical"> <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>
主界面大概这就可以了,剩下的就是要动态添加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("http://api.tianapi.com/") //String .addConverterFactory(ScalarsConverterFactory.create()) .addConverterFactory(GsonConverterFactory.create())//添加 json 转换器 // compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0' .addCallAdapterFactory(RxJavaCallAdapterFactory.create())//添加 RxJava 适配器 .build(); ApiService apiManager = retrofit.create(ApiService.class);//这里采用的是Java的动态代理模式 apiManager.getNewsData("你的APIKREY", "10", page) .subscribeOn(Schedulers.io()) .map(new Func1<NewsGson, List<News>>() { @Override public List<News> call(NewsGson newsgson) { // List<News> newsList = new ArrayList<News>(); for (NewsGson.NewslistBean newslistBean : newsgson.getNewslist()) { News new1 = new News(); new1.setTitle(newslistBean.getTitle()); new1.setCtime(newslistBean.getCtime()); new1.setDescription(newslistBean.getDescription()); new1.setPicUrl(newslistBean.getPicUrl()); new1.setUrl(newslistBean.getUrl()); newsList.add(new1); } return newsList; // 返回类型 } }) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Subscriber<List<News>>() { @Override public void onNext(List<News> newsList) { adapter.addAll(newsList); } @Override public void onCompleted() { } @Override public void onError(Throwable e) { Toast.makeText(getContext(), "网络连接失败", Toast.LENGTH_LONG).show(); } }); page = page + 1; }
使用retrofit 发起网络请求
数据通过rxjava提交先在io线程里,返回到主线程
中间设置map 转换 把得到的Gson类转化为所需的News类(可以省略这一步)
subscribe的onNext里处理返回的最终数据。
关于建立Gson类
Gson是谷歌的Json处理包,添加依赖。compile 'com.google.code.gson:gson:2.7'
配合插件:GsonFormat可以快速通过json数据建立对应类。
my.gif
数据绑定到recyview
由于我们使用的是被扩展的recyview,所以用起来很方便。
具体使用去作者的githuaEasyRecyclerView
Adapter
继承recycle的adapter,主要返回自己的ViewHolderpublic class NewsAdapter extends RecyclerArrayAdapter<News> { public NewsAdapter(Context context) { super(context); } @Override public BaseViewHolder OnCreateViewHolder(ViewGroup parent, int viewType) { return new NewsViewHolder(parent); } }
ViewHolder
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); } }
设置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); } });
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="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.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>
CoordinatorLayout+AppBarLayout里面配合CollapsingToolbarLayout布局技能实现toolbar的动画:
my.gif
上面的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); }
到这里基本完成:最后动态添加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
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
:
my.gif
技巧:鼠标要移动到布局文件名上。
文件已发至Github---https://github.com/HuRuWo/YiLan
作者:FuckskyZhao
用户评论