您的位置:首页 > 移动开发

Android 实践:做一款新闻 APP

2017-05-06 11:17 176 查看
跟代码相关的工作,大多唯手熟尔,所以这里花了点时间做了款简易版的新闻 APP,虽然都是些基础的内容,不过还是可以加深自己对部分代码的理解。至少,可以加深自己的记忆

步骤

依赖库

网络请求

网络解析

界面布局

最后

运行界面

运行GIF

完整代码下载地址(github)

依赖库

过程中需要用到一些开源依赖库文件,先在 build.grade 中声明:

compile 'com.google.code.gson:gson:2.8.0'       //网络解析
compile 'com.squareup.okhttp3:okhttp:3.7.0'     //网络请求
compile 'com.github.bumptech.glide:glide:3.8.0' //图片加载
compile 'com.android.support:design:24.2.1'     //Material Design中用到的依赖库
compile 'de.hdodenhof:circleimageview:2.1.0'    //显示圆形图片


网络请求

在包下创建一个文件夹 util 用来存放工具类,创建文件 HttpUtil.class 用来请求数据:

public class HttpUtil {
public static void sendOkHttpRequest(String address, okhttp3.Callback callback){
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(address).build();
client.newCall(request).enqueue(callback);
}
}


这里用到的是 okhttp3.Callback 的回调接口,结果会返回到 callback 的回调函数中,后面会进行处理

网络解析

我们先从数据解析开始,毕竟这才是这个小项目的重点。这次项目使用的数据来源是天行数据(http://www.tianapi.com/ )的新闻资讯 API ,先看 API 的说明:



可以看到返回数据为 JSON, 默认返回 10 条参数。请求地址为:



其中, APIKEY 需要用个人的 API KEY 代替,可以在个人中心中看到,其他的请求地址也是大同小异



JSON 返回示例:



还有错误返回码,用来判断返回数据的异常情况:



根据 gson 的返回示例,我们可以写出对应的实体类文件,通过 gson 将返回数据转化为对应的类型。先创建一个 gson 文件夹存放实体类文件。

在 gson 文件夹下创建 New.class 文件:

public class News {
@SerializedName("ctime")
public String time;

public String title;

public String description;

public String picUrl;

public String url;

}


创建 NewsList.class 文件:

public class NewsList {

public int code;

public String msg;

@SerializedName("newslist")
public List<News> newsList ;

}


至此,我们就已经创建好了与返回数据对应的实体类。

在 util 文件夹下创建文件 Utility.class 文件:

public class Utility {
public static NewsList parseJsonWithGson(final String requestText){
Gson gson = new Gson();
return gson.fromJson(requestText, NewsList.class);
}

}


将请求得到的数据解析为 NewList 实体类对象。现在网络请求和解析都准备好了,就开始界面文件了

界面布局

修改 values 目录下的 styles.xml 文件:

<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimary</item>
<item name="colorAccent">@color/colorAccent</item>
</style>

</resources>


修改通知栏颜色和标题栏颜色一样,是处于视觉统一的原因,也可以不修改(非必须)

主要采用的是 Material Design 的设计,整体布局采用的是滑动菜单,主界面内容为 ToolBar 和 ListView(这里为了方便,就直接使用),侧边栏内容为 NavigationView

主界面:

因为要用 ToolBar 替代 ActionBar, 我们先修改 values 下面的 styles 文件,修改主题为:

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">


在layout 下创建 nav_header 文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="180dp"
android:background="@color/colorPrimary"
android:padding="10dp">

<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/icon_image"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_centerInParent="true"
android:src="@drawable/nav_icon" />

<TextView
android:id="@+id/username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="https://github.com/lentitude"
android:textColor="@color/color_White"
android:textSize="14sp" />

<TextView
android:id="@+id/mail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/username"
android:text="lentitude"
android:textColor="@color/color_White"
android:textSize="14sp" />

</RelativeLayout>


这里在头部文件中放置了一个CircleImageView,两个 TextView,没有什么理解难度

在 res 目录下创建 menu 文件夹,新建 nav_menu.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_society"
android:title="社会新闻" />
<item
android:id="@+id/nav_county"
android:title="国内新闻" />
<item
android:id="@+id/nav_internation"
android:title="国际新闻" />
<item
android:id="@+id/nav_fun"
android:title="娱乐新闻" />
<item
android:id="@+id/nav_sport"
android:title="体育新闻" />
<item
android:id="@+id/nav_nba"
android:title="NBA新闻" />
<item
android:id="@+id/nav_football"
android:title="足球新闻" />
<item
android:id="@+id/nav_technology"
android:title="科技新闻" />
<item
android:id="@+id/nav_work"
android:title="创业新闻" />
<item
android:id="@+id/nav_apple"
android:title="苹果新闻" />
<item
android:id="@+id/nav_war"
android:title="军事新闻" />
<item
android:id="@+id/nav_internet"
android:title="移动互联" />
<item
android:id="@+id/nav_travel"
android:title="旅游咨询" />
<item
android:id="@+id/nav_health"
android:title="健康知识" />
<item
android:id="@+id/nav_strange"
android:title="奇闻异事" />
<item
android:id="@+id/nav_looker"
android:title="美女图片" />
<item
android:id="@+id/nav_vr"
android:title="VR科技" />
<item
android:id="@+id/nav_it"
android:title="IT资讯" />

</group>
</menu>


这里创建了若干个 ITEM 子项,只有 title,没有 icon,大家可以自行放置

主界面 activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
>

<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">

<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">

<android.support.v7.widget.Toolbar
android:id="@+id/tool_bar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:contentInsetStart="0dp"
app:titleTextColor="@color/color_White"
android:background="@color/colorPrimary"
/>

</android.support.design.widget.AppBarLayout>

<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipe_layout"
android:layout_width="match_parent"
a
15d82
ndroid:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">

<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@color/color_Background"
android:dividerHeight="1dp"

/>

</android.support.v4.widget.SwipeRefreshLayout>

</android.support.design.widget.CoordinatorLayout>

<android.support.design.widget.NavigationView
android:id="@+id/nav_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="@layout/nav_header"
app:menu="@menu/nav_menu"
/>

</android.support.v4.widget.DrawerLayout>


因为是一步到位,所以……大家最好之前用过使用过相同的布局设计(比如:第一行代码)

DrawerLayout 中有两个直接子布局文件:

1. CoordinatorLayout:一种 FrameLayout, 作为显示主界面内容的最外层布局

2. NavigationView:作为显示侧边栏的最外层布局,不过已经封装好了,可以直接通过 app:headerLayout 和 app:menu 属性引用之前我们已经写好的 头部和菜单布局文件

CoordinatorLayout 中有两个直接子布局文件:

1. AppBarLayout :通过 AppBarLayout 属性,可以将 ToolBar 和 ListView 分隔开,可以对滚动事件进行响应,实现 Material 效果

2. SwipeRefreshLayout:用来刷新 ListView 中的内容

创建 list_view_item.xml 文件,设计 ListView 的子项布局:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="@color/color_White">

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp">

<ImageView
android:id="@+id/title_pic"
android:layout_width="80dp"
android:layout_height="60dp"
android:layout_centerVertical="true"
android:layout_alignParentRight="true"
android:scaleType="centerCrop"/>

<TextView
android:id="@+id/title_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
android:layout_marginRight="10dp"
android:layout_alignTop="@+id/title_pic"
android:layout_alignParentLeft="true"
android:layout_toLeftOf="@+id/title_pic"
/>

<TextView
android:id="@+id/descr_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="8sp"
android:layout_marginRight="10dp"
android:layout_alignBottom="@+id/title_pic"
android:layout_alignParentLeft="true"
/>

</RelativeLayout>

</RelativeLayout>


子项布局内包含 3 个控件,ImageView 显示返回的图片,TextView 显示返回的标题和出处

创建一个 Title.class类:

public class Title {
private String title;
private String descr;
private String imageUrl;
private String uri;

public Title(String title,String descr, String imageUrl, String uri){
this.title = title;
this.imageUrl = imageUrl;
this.descr = descr;
this.uri = uri;
}

public String getTitle() {
return title;
}

public String getImageUrl() {
return imageUrl;
}

public String getDescr() {
return descr;
}

public String getUri() {
return uri;
}
}


这里之所以除了 标题,出处,图片显示在 ListViw 中,uri 传入另一个布局,显示内容文件

接下来就是 TitleAdapter.class

public class TitleAdapter extends ArrayAdapter<Title> {
private int resourceId;

public TitleAdapter(@NonNull Context context, @LayoutRes int resource, @NonNull List<Title> objects) {
super(context, resource, objects);
resourceId = resource;
}

@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
Title title = getItem(position);
View view;
ViewHolder viewHolder;
/**
* 缓存布局和实例,优化 listView
*/
if (convertView == null){
view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
viewHolder = new ViewHolder();
viewHolder.titleText = (TextView)view.findViewById(R.id.title_text);
viewHolder.titlePic = (ImageView) view.findViewById(R.id.title_pic);
viewHolder.titleDescr = (TextView)view.findViewById(R.id.descr_text);
view.setTag(viewHolder);
}else{
view = convertView;
viewHolder = (ViewHolder) view.getTag();
}

Glide.with(getContext()).load(title.getImageUrl()).into(viewHolder.titlePic);
viewHolder.titleText.setText(title.getTitle());
viewHolder.titleDescr.setText(title.getDescr());

return view;

}

public class ViewHolder{
TextView titleText;
TextView titleDescr;
ImageView titlePic;
}
}


这里还是一样的老套路,通过convertView 来缓存布局,通过类 ViewHolder 缓存控件实例,这样做,可以节省 50% 的效率,所以还是按照老套路走吧。

接下来就是 Activity 文件 MainActivity.class:

public class MainActivity extends AppCompatActivity {
private static final int  ITEM_SOCIETY= 1;
private static final int  ITEM_COUNTY= 2;
private static final int  ITEM_INTERNATION= 3;
private static final int  ITEM_FUN= 4;
private static final int  ITEM_SPORT= 5;
private static final int  ITEM_NBA= 6;
private static final int  ITEM_FOOTBALL= 7;
private static final int  ITEM_TECHNOLOGY= 8;
private static final int  ITEM_WORK= 9;
private static final int  ITEM_APPLE= 10;
private static final int  ITEM_WAR= 11;
private static final int  ITEM_INTERNET= 12;
private static final int  ITEM_TREVAL= 13;
private static final int  ITEM_HEALTH= 14;
private static final int  ITEM_STRANGE= 15;
private static final int  ITEM_LOOKER= 16;
private static final int  ITEM_VR= 17;
private static final int  ITEM_IT= 18;

private List<Title> titleList = new ArrayList<Title>();
private ListView listView;
private TitleAdapter adapter;
private NavigationView navigationView;
private DrawerLayout drawerLayout;
private SwipeRefreshLayout refreshLayout;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

Toolbar toolbar = (Toolbar)findViewById(R.id.tool_bar);
setSupportActionBar(toolbar);
final ActionBar actionBar = getSupportActionBar();
if (actionBar != null){
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setHomeAsUpIndicator(R.drawable.ic_menu);
}
actionBar.setDisplayShowTitleEnabled(true);
actionBar.setTitle("社会新闻");

refreshLayout = (SwipeRefreshLayout)findViewById(R.id.swipe_layout);
refreshLayout.setColorSchemeColors(getResources().getColor(R.color.colorPrimary));
listView = (ListView)findViewById(R.id.list_view);
adapter = new TitleAdapter(this,R.layout.list_view_item, titleList);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
Intent intent = new Intent(MainActivity.this, ContentActivity.class);
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Title title = titleList.get(position);
intent.putExtra("title",actionBar.getTitle());
intent.putExtra("uri",title.getUri());
startActivity(intent);
}
});

drawerLayout = (DrawerLayout)findViewById(R.id.drawer_layout);
navigationView = (NavigationView)findViewById(R.id.nav_view);
navigationView.setCheckedItem(R.id.nav_society);
navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()){
case R.id.nav_society:
handleCurrentPage("社会新闻",ITEM_SOCIETY);
break;
case R.id.nav_county:
handleCurrentPage("国内新闻",ITEM_COUNTY);
break;
case R.id.nav_internation:
handleCurrentPage("国际新闻",ITEM_INTERNATION);
break;
case R.id.nav_fun:
handleCurrentPage("娱乐新闻",ITEM_FUN);
break;
case R.id.nav_sport:
handleCurrentPage("体育新闻",ITEM_SPORT);
break;
case R.id.nav_nba:
handleCurrentPage("NBA新闻",ITEM_NBA);
break;
case R.id.nav_football:
handleCurrentPage("足球新闻",ITEM_FOOTBALL);
break;
case R.id.nav_technology:
handleCurrentPage("科技新闻",ITEM_TECHNOLOGY);
break;
case R.id.nav_work:
handleCurrentPage("创业新闻",ITEM_WORK);
break;
case R.id.nav_apple:
handleCurrentPage("苹果新闻",ITEM_APPLE);
break;
case R.id.nav_war:
handleCurrentPage("军事新闻",ITEM_WAR);
break;
case R.id.nav_internet:
handleCurrentPage("移动互联",ITEM_INTERNET);
break;
case R.id.nav_travel:
handleCurrentPage("旅游资讯",ITEM_TREVAL);
break;
case R.id.nav_health:
handleCurrentPage("健康知识",ITEM_HEALTH);
break;
case R.id.nav_strange:
handleCurrentPage("奇闻异事",ITEM_STRANGE);
break;
case R.id.nav_looker:
handleCurrentPage("美女图片",ITEM_LOOKER);
break;
case R.id.nav_vr:
handleCurrentPage("VR科技",ITEM_VR);
break;
case R.id.nav_it:
handleCurrentPage("IT资讯",ITEM_IT);
break;
default:
break;
}
drawerLayout.closeDrawers();
return true;
}
});

refreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
refreshLayout.setRefreshing(true);
int itemName = parseString((String)actionBar.getTitle());
requestNew(itemName);
}
});

requestNew(ITEM_SOCIETY);

}

/**
*  判断是否是当前页面,如果不是则 请求处理数据
*/
private void handleCurrentPage(String text, int item){
ActionBar actionBar = getSupportActionBar();
if (!text.equals(actionBar.getTitle().toString())){
actionBar.setTitle(text);
requestNew(item);
refreshLayout.setRefreshing(true);
}
}

/**
* 请求处理数据
*/
public void requestNew(int itemName){

// 根据返回到的 URL 链接进行申请和返回数据
String address = response(itemName);    // key
HttpUtil.sendOkHttpRequest(address, new Callback() {
@Override
public void onFailure(Call call, IOException e) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "新闻加载失败", Toast.LENGTH_SHORT).show();
}
});
}

@Override
public void onResponse(Call call, Response response) throws IOException {
final String responseText = response.body().string();
final NewsList newlist = Utility.parseJsonWithGson(responseText);
final int code = newlist.code;
final String msg = newlist.msg;
if (code == 200){
titleList.clear();
for (News news:newlist.newsList){
Title title = new Title(news.title,news.description,news.picUrl, news.url);
titleList.add(title);
}

runOnUiThread(new Runnable() {
@Override
public void run() {
adapter.notifyDataSetChanged();
listView.setSelection(0);
refreshLayout.setRefreshing(false);
};
});
}else{
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "数据错误返回",Toast.LENGTH_SHORT).show();
refreshLayout.setRefreshing(false);
}
});
}

}
});

}

/**
* 输入不同的类型选项,返回对应的 URL 链接
*/
private String response(int itemName){
String address = "https://api.tianapi.com/social/?key=339a8b166f397f008236e596616a5f54&num=50&rand=1";
switch(itemName){
case ITEM_SOCIETY:
break;
case ITEM_COUNTY:
address = address.replaceAll("social","guonei");
break;
case ITEM_INTERNATION:
address = address.replaceAll("social","world");
break;
case ITEM_FUN:
address = address.replaceAll("social","huabian");
break;
case ITEM_SPORT:
address = address.replaceAll("social","tiyu");
break;
case ITEM_NBA:
address = address.replaceAll("social","nba");
break;
case ITEM_FOOTBALL:
address = address.replaceAll("social","football");
break;
case ITEM_TECHNOLOGY:
address = address.replaceAll("social","keji");
break;
case ITEM_WORK:
address = address.replaceAll("social","startup");
break;
case ITEM_APPLE:
address = address.replaceAll("social","apple");
break;
case ITEM_WAR:
address = address.replaceAll("social","military");
break;
case ITEM_INTERNET:
address = address.replaceAll("social","mobile");
break;
case ITEM_TREVAL:
address = address.replaceAll("social","travel");
break;
case ITEM_HEALTH:
address = address.replaceAll("social","health");
break;
case ITEM_STRANGE:
address = address.replaceAll("social","qiwen");
break;
case ITEM_LOOKER:
address = address.replaceAll("social","meinv");
break;
case ITEM_VR:
address = address.replaceAll("social","vr");
break;
case ITEM_IT:
address = address.replaceAll("social","it");
break;
default:
}
return address;
}

/**
* 通过 actionbar.getTitle() 的参数,返回对应的 ItemName
*/
private int parseString(String text){
if (text.equals("社会新闻")){
return ITEM_SOCIETY;
}
if (text.equals("国内新闻")){
return ITEM_COUNTY;
}
if (text.equals("国际新闻")){
return ITEM_INTERNATION;
}
if (text.equals("娱乐新闻")){
return ITEM_FUN;
}
if (text.equals("体育新闻")){
return ITEM_SPORT;
}
if (text.equals("NBA新闻")){
return ITEM_NBA;
}
if (text.equals("足球新闻")){
return ITEM_FOOTBALL;
}
if (text.equals("科技新闻")){
return ITEM_TECHNOLOGY;
}
if (text.equals("创业新闻")){
return ITEM_WORK;
}
if (text.equals("苹果新闻")){
return ITEM_APPLE;
}
if (text.equals("军事新闻")){
return ITEM_WAR;
}
if (text.equals("移动互联")){
return ITEM_INTERNET;
}
if (text.equals("旅游资讯")){
return ITEM_TREVAL;
}
if (text.equals("健康知识")){
return ITEM_HEALTH;
}
if (text.equals("奇闻异事")){
return ITEM_STRANGE;
}
if (text.equals("美女图片")){
return ITEM_LOOKER;
}
if (text.equals("VR科技")){
return ITEM_VR;
}
if (text.equals("IT资讯")){
return ITEM_IT;
}
return ITEM_SOCIETY;
}

/**
* 对侧边栏按钮进行处理,打开侧边栏
*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()){
case android.R.id.home:
drawerLayout.openDrawer(GravityCompat.START);
break;
default:
}
return true;
}

/**
* 对返回键进行处理,如果侧边栏打开则关闭侧边栏,否则关闭 activity
*/
@Override
public void onBackPressed() {
if(drawerLayout.isDrawerOpen(GravityCompat.START)){
drawerLayout.closeDrawers();
}else{
finish();
}
}
}


本文的代码量虽然很大,只是比较繁琐,因为需要根据点击的 ITEM 来对不同的 接口地址提出申请,大部分的函数功能都有进行注释,所以略过了

public void requestNew(int itemName){

// 根据返回到的 URL 链接进行申请和返回数据
String address = response(itemName);    // key
HttpUtil.sendOkHttpRequest(address, new Callback() {
@Override
public void onFailure(Call call, IOException e) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "新闻加载失败", Toast.LENGTH_SHORT).show();
}
});
}

@Override
public void onResponse(Call call, Response response) throws IOException {
final String responseText = response.body().string();
final NewsList newlist = Utility.parseJsonWithGson(responseText);
final int code = newlist.code;
final String msg = newlist.msg;
if (code == 200){
titleList.clear();
for (News news:newlist.newsList){
Title title = new Title(news.title,news.description,news.picUrl, news.url);
titleList.add(title);
}

runOnUiThread(new Runnable() {
@Override
public void run() {
adapter.notifyDataSetChanged();
listView.setSelection(0);
refreshLayout.setRefreshing(false);
};
});
}else{
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "数据错误返回",Toast.LENGTH_SHORT).show();
refreshLayout.setRefreshing(false);
}
});
}

}
});

}


外部调用时传入 itemName 参数,通过 response() 函数得到所需要请求数据的地址

通过sendOkHttpRequest() 回调方法,在返回数据成功的 onResponse() 方法中使用 parseJsonWithGson() 方法获取对应的实体类

将实体类中的数据添加到 Title对应中,将 Title 对象添加到 titleList 中,最后通过 runOnUiThread() 方法,切换到主线程提醒适配器进行数据更新。

至此,主界面的代码逻辑都已经处理好了,还有 ListView 子项布局的点击事件处理:

listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
Intent intent = new Intent(MainActivity.this, ContentActivity.class);
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Title title = titleList.get(position);
intent.putExtra("title",actionBar.getTitle());
intent.putExtra("uri",title.getUri());
startActivity(intent);
}
});


在点击 ListView 子项布局时,会传入 标题栏文本 和 内容 URL

文件 activity_content.xml:

<?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:background="@color/color_White">

<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>

<android.support.v7.widget.Toolbar
android:id="@+id/tool_bar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:titleTextColor="@color/color_White"
app:theme="@style/ThemeOverlay.AppCompat.Light"
app:layout_scrollFlags="enterAlways|snap|scroll"/>

</android.support.design.widget.AppBarLayout>

<WebView
android:id="@+id/web_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

</android.support.design.widget.CoordinatorLayout>


如果了解了 activity_main.xml 的布局,这个布局也就没什么难度了,主要是新增了 WebView 控件,用来显示传入的 URL

文件 ContentActivity.class:

public class ContentActivity extends AppCompatActivity {
private WebView webView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_content);

Toolbar toolbar = (Toolbar)findViewById(R.id.tool_bar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null){
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setHomeAsUpIndicator(R.drawable.ic_back);
}

webView = (WebView)findViewById(R.id.web_view);
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebViewClient(new WebViewClient());

String uri = getIntent().getStringExtra("uri");
String title = getIntent().getStringExtra("title");
actionBar.setDisplayShowTitleEnabled(true);
actionBar.setTitle(title);
webView.loadUrl(uri);

}

/**
* 点击返回键做了处理
*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()){
case android.R.id.home:
finish();
break;
default:
}
return true;
}
}


显示传入的 URL网址

最后

到这里就结束了 ? 如果你认为都结束了,那你可以就需要面对打开应用之后马上闪退的情况了…….权限

我们还没有对权限进行申请,在 AndroidManifest 文件中添加声明:

<uses-permission android:name="android.permission.INTERNET"/>


不过个人还是建议把 权限的考虑放在最先的优先级,毕竟养成这个习惯,就可以专注于代码的 bug…………………..

运行界面



(主界面)



(侧边栏)



(内容界面)

运行 GIF



(由于大小限制,所以就只能传这么大了)

完整代码下载地址

https://github.com/lentitude/NewsMD
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: