【小白做專案-02】
本文為Karthus77原創,轉載請說明
文章目錄
- 【小白做專案-02】
- 一、專案需求
- 二、效果展示
- 三、專案分析
- 四、實戰操作
- 1.專案框架搭建
- 1.1 Activity搭建
- 1.2 添加網路訪問權限
- 1.3 添加依賴
- 2.UI界面搭建
- 2.1界面部分
- 2.2狀態欄透明效果設定
- 3.Recyclerview使用
- 3.1 Recyclerview作業原理
- 3.2 布局檔案中Recyclerview
- 3.3 創建item布局
- 3.4 創建Adapter
- 4.網路訪問獲取資料
- 4.1 Calender日歷獲取時間
- 4.2 發送網路請求
- 4.3 Json資料決議與Map,list儲存
- 4.4 Adapter適配與多item
- 5.重繪與加載
- 6.WebView的使用
- 7.評論展示與glide工具>
- 8.斷網處理
- 五、總結
一、專案需求
- 仿照“知乎日報APP”設計
- 輪播圖不做要求
- 評論樓中樓不做要求
- 用戶系統不做要求
二、效果展示
三、專案分析
仿照知乎日報這個專案,是一個較為復雜的APP專案
該專案的核心是“資料獲取”到“資料展示”的一個程序
核心知識點如下:
- Recyclerview多item
- Recyclview使用
- Json資料決議
- webview使用
- smartrefresh的使用
- 網路訪問獲取資料
- glide工具加載圖片
四、實戰操作
1.專案框架搭建
1.1 Activity搭建
該專案需要3個活動頁面
- 主頁面(展示新聞標題)
- 內容頁面(展示具體新聞內容)
- 評論頁面(展示用戶評論)
1.2 添加網路訪問權限

在AndroidManifest.xml檔案中加入以下兩行代碼,
并在<application下方加入以下代碼
android:usesCleartextTraffic="true"
AndroidManifest是對應Android應用的一個組態檔,例如我在該檔案中加入了聯網許可,這個APP就可以聯網,
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
1.3 添加依賴
首先,什么是依賴,為什么要去添加它呢?
簡單的來說,依賴是一個程式,是一堆代碼,這段程式或代碼可以幫助我們解決很多問題,比如我們在寫C語言程式是,要include頭檔案,頭檔案中就已經為我們寫好了很多函式的定義,依賴就是如此,Android Studio內部沒有實作的功能,我們以依賴方式添加,就可以實作依賴中的功能,
需要說明的是,依賴雖好,可不要頻繁使用,因為依賴是別人寫出來的,內部的實作功能你了解的并不清晰,并不理解,此種利弊,需要自己好好考量
在build.gradle中加入以下依賴(glide圖片加載工具,smartrefresh重繪工具,recyclerview視圖工具)
implementation 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.2'
implementation 'com.scwang.smartrefresh:SmartRefreshHeader:1.1.2'
2.UI界面搭建
2.1界面部分
由于UI界面參考“知乎日報APP”我們只需要模仿即可
我們只需要做出圖片中紅線包裹的UI即可
2.2狀態欄透明效果設定
我們可以看到紅框部分狀態欄是圖片背景的底色
設定方法,在xml檔案對應的Activity的onCreate周期后面加入以下代碼即可實作該效果
if (Build.VERSION.SDK_INT >= 21){
View decorView = getWindow().getDecorView();
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
getWindow().setStatusBarColor(Color.TRANSPARENT);
}
3.Recyclerview使用
什么是Recyclerview呢?它有什么用呢?
通俗的話,Recyclerview就是一個強大的控制元件,用于重復展示某一類東西,展示的每一個東西叫做item
如圖中所示,每一個新聞就是Recyclerview中的一個item,同樣在我們QQ聊天為微信聊天的地方,每一個聯系人的小窗就是一個item,有了Recyclerview我們就可以做出知乎日報中新聞的效果了
3.1 Recyclerview作業原理
我們說Recyclerview是一個控制元件,也就是說它是類似與Textview,Editview的一種工具,我們在Activity中對Textview等可以進行一系列的操作,同樣,我們在Activity對Recyclerview進行操作,
Recyclerview既然要以一個形式重復展示多組資料(item),所以Recyclerview控制元件的本質功能就是“資料接收”到“資料展示”的一個工具,
那么如何進行資料的接受呢?資料的接受必須以一定的形式存貯起來,我們知道資料存貯的方式有陣列,有鏈表,有結構體,而資料的展示就要用到Adapter配接器,即根據資料適配對應的展示形式,
Adapter能夠將Recyclerview和Activity聯系在一起,來展示資訊,
3.2 布局檔案中Recyclerview
在xml檔案中加入Recyclerview
<com.scwang.smartrefresh.layout.SmartRefreshLayout
android:id="@+id/refreshLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</com.scwang.smartrefresh.layout.SmartRefreshLayout>
由于需要重繪和加載功能,我們提前在Recyclerview中包裹我們的smartfresh
3.3 創建item布局
在 layout檔案中新建一個layout XML file 檔案用于寫出每一條新聞檔案
分析每一條新聞布局
可以看出每一個item有標題,小標題和圖片三部分
這里給出該item的xml的代碼
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/paper_item"
android:layout_width="match_parent"
android:layout_height="105dp">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="100dp">
<TextView
android:id="@+id/titleArticle"
android:layout_width="224sp"
android:layout_height="wrap_content"
android:layout_marginLeft="15sp"
android:textSize="16sp"
android:layout_marginTop="35sp"
android:textStyle="bold"
android:text="小事·懷孕,怕嗎?"
android:textColor="@color/black">
</TextView>
<TextView
android:id="@+id/viceTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="12sp"
android:layout_below="@id/titleArticle"
android:layout_marginTop="5sp"
android:text="VOL·1244"
android:layout_marginLeft="15sp">
</TextView>
<ImageView
android:id="@+id/imageTitle"
android:layout_width="75sp"
android:layout_height="75sp"
android:layout_alignParentRight="true"
android:layout_marginTop="25sp"
android:layout_marginRight="15sp">
</ImageView>
<TextView
android:id="@+id/everyday"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/viceTitle"
android:text=""
android:textSize="10sp"
android:layout_marginLeft="50sp"
android:layout_marginTop="10sp">
</TextView>
</RelativeLayout>
</LinearLayout>
3.4 創建Adapter

新建Java Class 并命名為MyAdapater用于新聞類展示
Adapater里面的具體代碼我們在后面添加,先把它創建好
4.網路訪問獲取資料
我們在前面已經為專案添加了網路訪問的權限
下面我們就進行網路訪問的操作
首先網路訪問需要進行在子執行緒而不是主執行緒中
因為網路訪問是一個耗時操作
新建子執行緒
final Thread thread=new Thread(new Runnable()
4.1 Calender日歷獲取時間
利用calender獲取當天的時間和早中晚來顯示紅框部分
代碼如下
final StringBuilder response = new StringBuilder();
final StringBuilder response1 = new StringBuilder();
String line;
String line1;
Calendar c=Calendar.getInstance();
int t = c.get(Calendar.HOUR_OF_DAY);
if(t>=18)
{
time.setText("晚上好!");
}
else if(t<=8&&t>=6)
{
time.setText("早上好!");
}
else
{
time.setText("知乎日報");
}
int d=c.get(Calendar.DAY_OF_MONTH);
String days=String.format("%d",d);
Date.setText(days);
int months=c.get(Calendar.MONTH);
switch (months+1)
{
case 1:month.setText("一月");
break;
case 2:month.setText("二月");
break;
case 3:month.setText("三月");
break;
case 4:month.setText("四月");
break;
case 5:month.setText("五月");
break;
case 6:month.setText("六月");
break;
case 7:month.setText("七月");
break;
case 8:month.setText("八月");
break;
case 9:month.setText("九月");
break;
case 10:month.setText("十月");
break;
case 11:month.setText("十一月");
break;
default:month.setText("十二月");
4.2 發送網路請求
我們已經新建了一個子執行緒,在這個子執行緒中進行聯網操作
先建立一個StringBulider獲取資料
StringBulider能夠將獲取的資料轉化成字串儲存起來
final StringBuilder response = new StringBuilder();
try {
URL url=new URL("https://news-at.zhihu.com/api/3/stories/latest");//獲取服務器哦地址
HttpURLConnection urlConnection= (HttpURLConnection) url.openConnection();//雙方建立連接
urlConnection.setRequestMethod("GET");//給服務器發送請求
InputStream inputStream=urlConnection.getInputStream(); //位元組流
Reader reader=new InputStreamReader(inputStream); //把位元組流轉化成字符流
BufferedReader bufferedReader=new BufferedReader(reader);//字符流 轉成 緩沖流,一次可以讀一行
while ((line=bufferedReader.readLine())!=null){//當temp讀到的資料為空就結束
response.append(line);
}
在這個程序中,url對應的是介面資料網站,setRequestMedthod設定訪問方式為get,利用readline函式以行方式讀取所有資料,讀完以后,所有資料已經存貯在response當中
4.3 Json資料決議與Map,list儲存
Json是一種易于理解的資料儲存形式,有著廣泛的應用,知乎日報官方Api回傳我們的是Json資料,因此我們需要將他決議,
我們用火狐瀏覽器打開介面網站,火狐瀏覽器會自動幫我們決議,便于理解
決議Json資料需要理解物件與陣列
我們以latest這個介面為例,stories對應了當天的6篇文章的Json資料,也就說stories對應了一個Json陣列,這個陣列中有6個物件
在每個物件中有文章的大小標題,和對應封面圖片的Url
下面給出決議代碼
runOnUiThread(new Runnable() {
@Override
public void run() {
try {
JSONObject jsonObject = new JSONObject(String.valueOf(response));
date=jsonObject.getInt("date");
int day=date%100;
String days=String.format("%d",day);
JSONArray jsonArray = jsonObject.getJSONArray("stories");
for (int i = 0; i < 6; i++) {
Map<String,Object> map=new HashMap<>();
JSONObject jsonObject1 = (JSONObject) jsonArray.getJSONObject(i);
String title = jsonObject1.getString("title");
String vicetitle = jsonObject1.getString("hint");
String url=jsonObject1.getString("url");
String id=jsonObject1.getString("id");
JSONArray jsonArray1=jsonObject1.getJSONArray("images");
String imageUrl=(String)jsonArray1.get(0);
map.put("url",url);
map.put("id",id);
map.put("image",imageUrl);
map.put("title", title);
map.put("hint",vicetitle);
list.add(map);
}
myAdapter = new MyAdapter(Paper.this,list);
recyclerView.setAdapter(myAdapter);
runOnUiThread是回傳主執行緒的操作,由于UI操作只能在主執行緒中操作,而我們的聯網操作是在子執行緒中進行的操作,而我們需要將Json資料用Recyclerview的形式展示是UI操作所以需要回傳UI執行緒,也就是主執行緒
資料儲存在list當中,并利用map鍵值對的方式方便我們在Adpater中資料的獲取
4.4 Adapter適配與多item
myAdapter = new MyAdapter(Paper.this,list);
recyclerView.setAdapter(myAdapter);
在Activity中就是這兩行代碼連接了Recyclerview和Adapter
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
private Paper context;
private List<Map<String,Object>> list;
private View inflater;
private AdapterView.OnItemClickListener onItemClickListener;
//構造方法,傳入資料
public MyAdapter(Paper context, List<Map<String,Object>> list){
this.context = context;
this.list = list;
}
private static final int news = 0;
private static final int date = 1;
private static final int noNet= 2;
@Override
public int getItemViewType(int position) {
int size=list.get(position).size();
if (size==1)
{
return date;
}
else if(size==2)
{
return noNet;
}
else
{
return news;
}
}
在這段代碼中,我們利用getItemViewtype獲得需要展示的視圖
我們可以看到這里有兩種不同的item,所以我們的Recyclerview還要能夠適配多種item形式,getItemViewType就是獲取需要的Item我們以每條list中的大小(有幾個map鍵值對)來判斷對應的item種類
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//創建ViewHolder,回傳每一項的布局
if(viewType==news)
{
inflater = LayoutInflater.from(context).inflate(R.layout.item,parent,false);
ViewHolder ViewHolder = new ViewHolder(inflater);
return ViewHolder;}
else if(viewType==noNet)
{
inflater=LayoutInflater.from(context).inflate(R.layout.item_nonet,parent,false);
noHolder noHolder=new noHolder(inflater);
return noHolder;
}
else
{
inflater = LayoutInflater.from(context).inflate(R.layout.item_date,parent,false);
dateHolder dateHolder = new dateHolder(inflater);
return dateHolder;
}
}
在onCreatViewHolder中系結對應的item
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
//將資料和控制元件系結
int viewType=getItemViewType(position);
if(viewType==0)
{
ViewHolder viewholder = (ViewHolder) holder;
String number_title=list.get(position).get("title").toString();
if(number_title.length()>27)
{
String title=number_title.substring(0,27);
viewholder.titlePaper.setText(title+"...");
}
else{
viewholder.titlePaper.setText(list.get(position).get("title").toString());
}
viewholder.titleVice.setText(list.get(position).get("hint").toString());
Glide.with(context).load(list.get(position).get("image")).into(viewholder.titleImage);
viewholder.paper_item.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent(context,Context.class);
Bundle bd=new Bundle();
bd.putString("id",list.get(position).get("id").toString());
bd.putString("url",list.get(position).get("url").toString());
intent.putExtras(bd);
context.startActivity(intent);
}
});}
else if(viewType==2)
{
noHolder noHolder=(noHolder) holder;
noHolder.hint.setText("網路好像走丟了");
}
else {
dateHolder viewHolder = (dateHolder) holder;
String day=list.get(position).get("date").toString();
int nowDay=(Integer.parseInt(day)%100);
int nowMonth=((Integer.parseInt(day)%10000)/100);
viewHolder.date.setText(nowMonth+" 月 "+nowDay+" 日");}
}
在onBindViewHolder中對需要用到的每一種item進行操作
@Override
public int getItemCount() {
//回傳Item總條數
return list.size();
}
//內部類,系結控制元件
class ViewHolder extends RecyclerView.ViewHolder{
LinearLayout paper_item;
TextView titlePaper;
TextView titleVice;
ImageView titleImage;
TextView everyday;
public ViewHolder(View view) {
super(view);
everyday=view.findViewById(R.id.everyday);
titlePaper= view.findViewById(R.id.titleArticle);
titleVice= view.findViewById(R.id.viceTitle);
titleImage = view.findViewById(R.id.imageTitle);
paper_item=view.findViewById(R.id.paper_item);
}
}
class dateHolder extends RecyclerView.ViewHolder{
TextView date;
TextView background;
public dateHolder (View view){
super(view);
date=view.findViewById(R.id.newDate);
background=view.findViewById(R.id.background);
}
}
class noHolder extends RecyclerView.ViewHolder{
TextView hint;
public noHolder (View view){
super(view);
hint =view.findViewById(R.id.hint);
}
}
創建內部類系結每一種布局
5.重繪與加載
重繪與加載,聽起來就是很高級的功能啊,
如何去理解它呢,重繪與加載的本質其實就是兩個點擊事件,只不過是以滑動的形式表現出來的罷了,
重繪和加載的實質也是資料的更新與加載,我們利用Recyclerview加載item使用到的list資料,list的資料變多了,Recyclerview顯示的item也會變多
refreshLayout.setOnRefreshListener
這段代碼用于重繪事件的展開
refreshLayout.setOnLoadMoreListener
這段代碼用于加載事件,下面給出加載部分的完整代碼
refreshLayout.setOnLoadMoreListener(new OnLoadMoreListener() {
@Override
public void onLoadMore(RefreshLayout refreshlayout) {
refreshlayout.finishLoadMore(2000/*,false*/);//傳入false表示加載失敗
final Thread thread=new Thread(new Runnable() {
@Override
public void run() {
final StringBuilder response1 = new StringBuilder();
String line1;
try {
yesterday=yesterday-1;
URL Url=new URL("https://news-at.zhihu.com/api/3/news/before/"+String.valueOf(yesterday));//獲取服務器哦地址
HttpURLConnection UrlConnection = (HttpURLConnection) Url.openConnection();//雙方建立連接
UrlConnection.setRequestMethod("GET");
InputStream inputStream1=UrlConnection.getInputStream();
Reader reader1=new InputStreamReader(inputStream1);
BufferedReader bufferedReader1=new BufferedReader(reader1);
while ((line1=bufferedReader1.readLine())!=null){//當temp讀到的資料為空就結束
response1.append(line1);
}
runOnUiThread(new Runnable() {
@Override
public void run() {
try {
JSONObject jsonObject = new JSONObject(String.valueOf(response1));
JSONArray jsonArray = jsonObject.getJSONArray("stories");
Map<String,Object> map1=new HashMap<>();
Integer day1=jsonObject.getInt("date");
map1.put("date",day1);
list.add(map1);
for (int i = 0; i < 6; i++) {
Map<String,Object> map=new HashMap<>();
JSONObject jsonObject1 = (JSONObject) jsonArray.getJSONObject(i);
String title = jsonObject1.getString("title");
String vicetitle = jsonObject1.getString("hint");
String url=jsonObject1.getString("url");
String id=jsonObject1.getString("id");
JSONArray jsonArray1=jsonObject1.getJSONArray("images");
String imageUrl=(String)jsonArray1.get(0);
map.put("url",url);
map.put("id",id);
map.put("image",imageUrl);
map.put("title", title);
map.put("hint",vicetitle);
list.add(map);
}
myAdapter.notifyDataSetChanged();
} catch (JSONException e) {
e.printStackTrace();
}
}
});
inputStream1.close();
reader1.close();
bufferedReader1.close();
} catch (Exception e) {
runOnUiThread(new Runnable() {
@Override
public void run() {
list.clear();
Map<String,Object> map=new HashMap<>();
map.put("1",1);
map.put("2",2);
list.add(map);
myAdapter = new MyAdapter(Paper.this,list);
recyclerView.setAdapter(myAdapter);
}
});
e.printStackTrace();
//這里要有聯網失敗提示
}
}
});
thread.start();//啟動執行緒
}
});
6.WebView的使用
顧名思義,webview即網頁視圖的意思
對于每篇文章的內容部分我們使用webView控制元件即可
WebView webView=findViewById(R.id.webView);
webView.getSettings().setJavaScriptEnabled(true);
webView.loadUrl(url);
僅僅三行代碼即可,其中的url是對應該文章的url地址
7.評論展示與glide工具>
glide的使用同樣需要添加依賴,我們已經在第一步的時候添加了glide的依賴,那么glide怎么使用呢
Glide.with(context).load(list.get(position).get("image")).into(viewholder.titleImage);
glide的使用只有一行代碼with部分是指當前的Activity,也就是context,load部分的get("image“),image是指所要加載圖片的url地址
評論部分的頭像圖片顯示為圓形,用glide工具可以加載圓形圖片
lide.with(context).load(list.get(position).get("image")).apply(RequestOptions.bitmapTransform(new CircleCrop())).into(viewholder.head);
用上面的代碼即可實作
評論部分的設計無需重繪加載,只需要用到多item即可,小伙伴們不妨自己嘗試去做
對了,每條評論下面的時間要自己寫演算法哦,因為介面傳過來的是一個很大的不知道怎么處理的數字,自己想想怎么能夠得到具體時間吧
8.斷網處理
我們的APP是需要聯網的,但是萬一你在沒有網的情況下打開它會怎么樣,
答案是:會閃退
所以我們需要做好斷網處理,斷網處理很簡單,我們在聯網操作時用到了
try{}catch結構
我們如果沒有聯網,程式會進入catch部分,所以我們在catch部分進行斷網處理即可
catch (Exception e) {
runOnUiThread(new Runnable() {
@Override
public void run() {
list.clear();
Map<String,Object> map=new HashMap<>();
map.put("1",1);
map.put("2",2);
list.add(map);
myAdapter = new MyAdapter(Paper.this,list);
recyclerView.setAdapter(myAdapter);
}
});
這是我catch部分的代碼,用于加載一個提示界面
效果如下
五、總結
總的來說,這是一個真正意義上的,可以說是一個成熟APP,作為練手專案,我很推薦,它有一定的難度,能夠充分考驗一個專案整體的架構能力,當然,整篇教程我在許多細節方面沒有詳細解答,因為我認為需要留有一部分自己思考,我認為能夠獨立做完這個專案,那么一定會對你的Android開發有著很大的提升,
該專案的源代碼和介面可以私信我即可,當然我可不是教大家做盜版軟體啊,純當小白自己練習,
下一個專案是一個淘寶類的專案,有個人用戶系統,可以上架購買商品,有興趣的小伙伴不妨關注一波~
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/258760.html
標籤:其他
上一篇:axios之post與get請求
下一篇:java開發環境的搭建









