Android 实践:做一款新闻 APP
跟代码相关的工作,大多唯手熟尔,所以这里花了点时间做了款简易版的新闻 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"><itemandroid:id="@+id/nav_society"android:title="社会新闻" /><itemandroid:id="@+id/nav_county"android:title="国内新闻" /><itemandroid:id="@+id/nav_internation"android:title="国际新闻" /><itemandroid:id="@+id/nav_fun"android:title="娱乐新闻" /><itemandroid:id="@+id/nav_sport"android:title="体育新闻" /><itemandroid:id="@+id/nav_nba"android:title="NBA新闻" /><itemandroid:id="@+id/nav_football"android:title="足球新闻" /><itemandroid:id="@+id/nav_technology"android:title="科技新闻" /><itemandroid:id="@+id/nav_work"android:title="创业新闻" /><itemandroid:id="@+id/nav_apple"android:title="苹果新闻" /><itemandroid:id="@+id/nav_war"android:title="军事新闻" /><itemandroid:id="@+id/nav_internet"android:title="移动互联" /><itemandroid:id="@+id/nav_travel"android:title="旅游咨询" /><itemandroid:id="@+id/nav_health"android:title="健康知识" /><itemandroid:id="@+id/nav_strange"android:title="奇闻异事" /><itemandroid:id="@+id/nav_looker"android:title="美女图片" /><itemandroid:id="@+id/nav_vr"android:title="VR科技" /><itemandroid: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.DrawerLayoutxmlns: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.CoordinatorLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><android.support.design.widget.AppBarLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"><android.support.v7.widget.Toolbarandroid: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.SwipeRefreshLayoutandroid:id="@+id/swipe_layout"android:layout_width="match_parent"android:layout_height="match_parent"app:layout_behavior="@string/appbar_scrolling_view_behavior"><ListViewandroid: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.NavigationViewandroid: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@Overridepublic 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;@Overrideprotected 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);@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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); // keyHttpUtil.sendOkHttpRequest(address, new Callback() {@Overridepublic void onFailure(Call call, IOException e) {runOnUiThread(new Runnable() {@Overridepublic void run() {Toast.makeText(MainActivity.this, "新闻加载失败", Toast.LENGTH_SHORT).show();}});}@Overridepublic 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() {@Overridepublic void run() {adapter.notifyDataSetChanged();listView.setSelection(0);refreshLayout.setRefreshing(false);};});}else{runOnUiThread(new Runnable() {@Overridepublic 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;}/*** 对侧边栏按钮进行处理,打开侧边栏*/@Overridepublic boolean onOptionsItemSelected(MenuItem item) {switch (item.getItemId()){case android.R.id.home:drawerLayout.openDrawer(GravityCompat.START);break;default:}return true;}/*** 对返回键进行处理,如果侧边栏打开则关闭侧边栏,否则关闭 activity*/@Overridepublic void onBackPressed() {if(drawerLayout.isDrawerOpen(GravityCompat.START)){drawerLayout.closeDrawers();}else{finish();}}
}
本文的代码量虽然很大,只是比较繁琐,因为需要根据点击的 ITEM 来对不同的 接口地址提出申请,大部分的函数功能都有进行注释,所以略过了
public void requestNew(int itemName){// 根据返回到的 URL 链接进行申请和返回数据String address = response(itemName); // keyHttpUtil.sendOkHttpRequest(address, new Callback() {@Overridepublic void onFailure(Call call, IOException e) {runOnUiThread(new Runnable() {@Overridepublic void run() {Toast.makeText(MainActivity.this, "新闻加载失败", Toast.LENGTH_SHORT).show();}});}@Overridepublic 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() {@Overridepublic void run() {adapter.notifyDataSetChanged();listView.setSelection(0);refreshLayout.setRefreshing(false);};});}else{runOnUiThread(new Runnable() {@Overridepublic 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);@Overridepublic 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.CoordinatorLayoutxmlns: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.AppBarLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"><android.support.v7.widget.Toolbarandroid: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><WebViewandroid: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;@Overrideprotected 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);}/*** 点击返回键做了处理*/@Overridepublic 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
Android 实践:做一款新闻 APP相关推荐
- Android 做一款直播APP?一分钟掌握乐视云直播Demo
最近工作需要做一款直播APP,恩是的,从RTMP协议的实现开始到处理服务器高并发.负载均衡.客户端播放器实现等等等..... 估计全部写完我也到而立之年了吧23333...... BOSS们估计也是发 ...
- Android安卓期末大作业 新闻app 实现注册登录增删改查功能
Android安卓期末大作业 新闻app (文末附下载链接) app情况如下图所示: 点我下载 https://download.csdn.net/download/weixin_43474701/7 ...
- 【Android】做一款类似我要当学霸里的学习监督的APP
我要当学霸这款App有个学习监督的功能,当你启动它的时候,你将无法使用其他App,以此达到帮助人提高自觉性,起到监督学习的效果.最近和同学做了个小App,正好有这个功能,所以就来说说它是怎么实现的. ...
- 练习项目 一款新闻app的开发 (二) : 新闻首页开发(整体UI架构)
下面主要整理下关于新闻首页的开发,最终效果图如下 本节主要先说下关于标题顶部栏和导航栏的UI处理,主要用到知识点有: 1.CoordinatorLayout : 用来协调子view. 具体详细描述,可 ...
- android学习的app,Android相见恨晚的6款学习APP,每天坚持学习,提升自我
学习对每个人来说都是非常重要的,因为不停的学习不停的进步才能让自己变得更好,要知道天才都是通过努力换来的,没有人生下来就是天才,所以我们也要努力去学习.今天就给大家分享6个实用学习类的APP. 1.中 ...
- Android Compose——一个简单的新闻APP
Owl 简述 效果视频 导航 导航结点 路线图 底部导航栏 使用 标签页 状态切换 FeaturePage 构建 CoursePage 实现 搜索 ViewModel View 详情页 Detail ...
- Android 实践:做一款可用的天气 APP
可能很多人会问:之前已经写过一篇博文来介绍怎么做一款简单的新闻APP(http://blog.csdn.net/yiwei12/article/details/71249628),为什么还要专门一篇来 ...
- html+css+js+Hbuilder开发一款安卓APP,根本不用学Android开发!
我们知道,要做一款安卓APP,咱们得先学安卓开发语言,例如java,前端后端.那么没有这些开发语言基础,咱们怎么做呢?其实现在有比较好的开发方案就是做webAPP,咱们可以用web前端知识构建安卓客户 ...
- html如何在手机上实现hb,html+css+js+Hbuilder开发一款安卓APP,根本不用学Android开发!...
我碎前整要们开自近事端个广的的带近事端个广们知道,要做一款安卓APP,咱们得先学安卓开发语言,例如java,前端后端.那么没有这些开发语言基础,咱们怎么做呢?其实现在有比较好的开发方案就是做webAP ...
最新文章
- 链接访问后刷新颜色回到初始_如何使链接可访问(提示:颜色不够)
- Handler的源码分析
- linux 标准函数注释,Linux 驱动程序中相关函数注释汇总(跟新中)
- 485通讯线是几芯的_RS485通讯线是几芯电缆
- 可视化信息论(2015年10月14日)
- 计算机网络体系结构中的环节,ppt课件-第二章计算机网络体系结构.ppt
- 帧同步(LockStep)该如何反外挂 及 优化
- 软件可靠性测试意义,软件可靠性测试方法与目的
- File.ReadAllText读取文件中文乱码
- 什么句型可以 让我说出 悲伤的文法
- CAD批量打图精灵入门教程--CAD批量打印、CAD批量转PDF
- 广东理工学院c语言考试试卷,20年广东理工学院成人高考期末考试 C语言程序设计 复习资料(15页)-原创力文档...
- 阿里云ECS云服务器Linux Tomcat启动慢 访问网页转圈
- mac搭建IPV6网络环境
- 网页无插件视频流媒体播放器EasyPlayerPro-IOS版如何解决有声音无画面的问题?
- pos 机 gd32f103 midi设备
- Computer Vision笔记01:图像处理
- ACM算法训练【贪心合集】
- PNP与NPN两种三极管使用方法
- 错误集(大概会持续更新)
热门文章
- Linux 基本使用和 web 程序部署
- 安卓录屏软件实现 开维PRA自动生成代码Ctrl.js
- 无符号格式化输出的区别%d,%u?
- 攀登规模化的高峰 - 蚂蚁集团大规模 Sigma 集群 ApiServer 优化实践
- arxiv数据_使用neo4j第1部分分析arxiv数据
- Python PyCharm Django 搭建web开发环境
- 17 重定向(Redirect) vs 转发(Forward)
- Cypher语句-Create语句
- Ceph 存储集群5-数据归置
- SMPL:数据增强之处理pose和3d点