前幾天打開了微信遊戲,找到了裏面有一部分內容非常適合用來做ConstraintLayout和RecyclerView的練習,然而前幾天一直在家或者摸魚,加上標註什麼的一直拖到了今天,那麼現在就來練練手。
*******************************************
讓我們來看看我截出來需要被實現的部分:
1、分析模塊
將每一個部分都做成一個子項,使用RecyclerView實現,其實和普通的RecyclerView一樣,只是將子項佈局改成了ConstraintLayout(我感覺這裏使用ConstraintLayout比使用RelativeLayout更方便一些)。下面是我對這個佈局內容的一些劃分:
紅色的線是輔助線,我用來完成佈局的對齊(在這裏其實感覺用不用都可以,但是GuideLine不得不說確實是個好東西)。綠色框內則是一個部分子佈局需要被隱藏的部分,因此決定將整個綠色框內寫在一個RelativeLayout之中。
2、實現
首先別忘了導入RecyclerView依賴庫!導入的方法之前有記錄過,可以在app的build.gradle中手打或者使用Project Structure添加。
步驟依舊是從需要被展現的信息開始。一個子項中被展現出來的內容如下:
但我們有的信息是可以自動生成的,如序號,綠框內的信息(模板語句是一樣的)。因此新建Game類,新建字段gameName和gameDesc,添加構造函數和返回函數(其實字段中也需要有gamePic,但是我懶得找圖片了,就沒有加進去)
public class Game {
private String gameName;
private String gameDesc;
public Game(String gameName, String gameDesc){
this.gameName = gameName;
this.gameDesc = gameDesc;
}
public String getGameName(){
return gameName;
}
public String getGameDesc(){
return gameDesc;
}
}
之後就要寫子項佈局了。新建game_item.xml,這裏我用的是ConstraintLayout:
設立兩條輔助線GuideLine,分別是距離12dp和38dp的劃分,就是上上圖中的兩條打豎的橫線,用來對齊子項內的部分元素。
<android.support.constraint.Guideline
android:id="@+id/twelveDpGuideLine"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintGuide_begin="12dp"/>
<android.support.constraint.Guideline
android:id="@+id/thirdtyEightDPGuideLine"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintGuide_begin="38dp" />
先用序號部分作爲一個示例開始寫吧。序號部分的數字是父佈局左側和38dp輔助線水平居中的,這是我的TextView:
<TextView
android:id="@+id/textNum"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="23sp"
android:textColor="#999"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="@id/thirdtyEightDPGuideLine"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="20dp"/>
其他的TextView和ImageView和Button基本同理,設置約束和margin等。
※對已經確定大小(寬高不是0dp)的控件進行左右相對約束則可以讓它水平居中(豎直居中同理)
※如果margin不起作用,請先確定是否有對margin的那個方向進行約束。可以回看ConstraintLayout使用方法和注意點那篇博文。
單獨分開綠色框那一部分,是因爲裏面的內容都是可能被一起隱藏掉的。這裏我用RelativeLayout包含裏面的內容,包括兩個TextView和一個分割線(View):
<RelativeLayout
android:id="@+id/textDetails"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/imgGamePic"
app:layout_constraintLeft_toRightOf="@id/thirdtyEightDPGuideLine"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginTop="9dp">
<TextView
android:id="@+id/textDetail1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#999"
android:textSize="12sp"
android:layout_marginLeft="9dp"/>
<TextView
android:id="@+id/textDetail2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/textDetail1"
android:textColor="#999"
android:textSize="12sp"
android:layout_marginTop="2dp"
android:layout_alignLeft="@id/textDetail1"/>
<View
android:layout_width="2dp"
android:layout_height="30dp"
android:background="#f1f1f1"/>
</RelativeLayout>
最後在底部加一條分界線,用來分來每個子項:
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:background="#dfdfdf"
app:layout_constraintLeft_toLeftOf="@id/twelveDpGuideLine"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/textDetails"
app:layout_goneMarginTop="14dp"
android:layout_marginTop="14dp"/>
爲什麼我覺得在這個item之中ConstraintLayout比RelativeLayout好用呢。由於我們的分界線的marginTop是相對於綠色框的RelativeLayout的內容,然而當我們隱藏了RelativeLayout後,View將會直接對齊RelativeLayout原來對齊的內容,也就是我上面對齊的gamePic。爲了解決這個問題,ConstraintLayout中有一個goneMargin屬性,它在約束控件隱藏的情況下被啓用,效果相當於margin。(但不知道爲什麼我這裏取消了goneMargin後,效果仍不變,也就是marginTop依舊存在,求解答)
這是我取消了marginTop之後不使用goneMarginTop和使用goneMarginTop的結果:
可以看出,goneMargin屬性確實只在被約束對象被隱藏後才啓用的。
那麼到這裏,子項佈局就完成了。接下來RecyclerView就和之前的步驟基本一樣。但因爲這裏除了前三項的綠色框內容(RelativeLayout)不需要被隱藏,三項以後的內容都不予顯示,因此在GameAdapter中的onBindViewHolder中加入判斷語句:
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Game game = gameList.get(position);
holder.gameNum.setText(String.valueOf(position+1));
holder.gameName.setText(game.getGameName());
holder.gameDesc.setText(game.getGameDesc());
if (position>2){
holder.gameDetails.setVisibility(View.GONE);
}else{
holder.gameDetail1.setText("最近超過"+ (new Random().nextInt(8)+1) + "百萬人在玩");
holder.gameDetail2.setText((new Random().nextInt(8)+1) +"萬人剛加入這款遊戲");
}
}
※ position是int類型的,而TextView接受的數據類型是CharSequence類型,因此一定要使用數據轉換將int轉換爲String,否則編譯將會報錯:android.content.res.Resources$NotFoundException: String resource ID #0x0
完成了所有工作之後,來看看整體佈局效果(這裏的圖片因爲我懶得找了,就直接用背景色代替了):
初步完成!