android仿支付寶首頁更多、應用編輯介面
阿新 • • 發佈:2019-02-20
[github地址](https://github.com/oldbirdy/recyclerdemo “github地址”)
專案越來越大,模組越來越多,首頁上展示的東西又不能全部都展示出來,只能選擇幾個重要的模組展示出來。但是不同的使用者關注的層面不一樣,只好讓使用者自己去選擇需要展示的應用。就像支付寶的應用編輯介面一樣。
整體介紹
總的來說實現了從json資料轉化為當前介面,可以自由的編輯介面儲存到本地。
主要介面佈局 一個標題欄,下面是一個RecyclerView(主要是拖拽實現很方便)。中間放了一個水平的HorizontalScrollView。下面是一個存放所有列表的RecyclerView
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg_white"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/title"
android:layout_width="fill_parent"
android:layout_height="48dp"
android:background="@drawable/bg_blue" >
<TextView
android:id="@+id/titletext"
android:textSize="20sp"
android:textColor="@color/white"
android:layout_width="wrap_content"
android:text="編輯我的應用"
android:gravity="center_vertical"
android:layout_height ="wrap_content"
android:layout_centerInParent="true" />
<TextView
android:id="@+id/submit"
android:textSize="20sp"
android:textColor="@color/white"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerInParent="true"
android:gravity="center_vertical"
android:text="儲存"/>
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:padding="10dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="我的應用"
android:textColor="#333333"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:text="(按住可拖動調整順序)"
android:textColor="#808080"
android:textSize="13sp" />
</LinearLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerViewExist"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingRight="10dp" />
<View
android:layout_width="match_parent"
android:layout_height="4dp"
android:background="@color/line_color">
</View>
<HorizontalScrollView
android:id="@+id/horizonLScrollView"
android:layout_width="match_parent"
android:layout_height="35dp"
android:scrollbarThumbHorizontal="@color/transparent"
android:scrollbars="none">
<RadioGroup
android:id="@+id/rg_tab"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="horizontal" />
</HorizontalScrollView>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:background="@color/line_color">
</View>
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerViewAll"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:paddingRight="10dp" />
</LinearLayout>
兩個實體類 tabItem(記錄每個tab項) 和FunctionItem(記錄每個功能模組)
public class TabItem {
private String tabName="";
private ArrayList<FunctionItem> functionItems;
}
public class FunctionItem {
public String name;
public boolean isSelect = false;
public String imageUrl = "";
public String background ="";
}
為了方便,通過GSON實現了測試資料。在庫檔案中。可以去生成測試資料。寫入到檔案中。
ArrayList<TabItem> tabItems = new ArrayList<>();
ArrayList<FunctionItem> arrayList = new ArrayList<>();
arrayList.add(new FunctionItem("充值中心",false,"icon_home_selected","#86c751"));
arrayList.add(new FunctionItem("信用卡還款",false,"icon_home_selected","#86c751"));
arrayList.add(new FunctionItem("生活繳費",false,"icon_home_selected","#86c751"));
arrayList.add(new FunctionItem("城市服務",false,"icon_home_selected","#86c751"));
arrayList.add(new FunctionItem("生活號",false,"icon_home_selected","#86c751"));
arrayList.add(new FunctionItem("我的客服",false,"icon_home_selected","#86c751"));
arrayList.add(new FunctionItem("我的快遞",false,"icon_home_selected","#86c751"));
arrayList.add(new FunctionItem("醫療健康",false,"icon_home_selected","#86c751"));
arrayList.add(new FunctionItem("記賬本",false,"icon_home_selected","#86c751"));
arrayList.add(new FunctionItem("城市一卡通",false,"icon_home_selected","#86c751"));
arrayList.add(new FunctionItem("發票管家",false,"icon_home_selected","#86c751"));
arrayList.add(new FunctionItem("螞蟻寶卡",false,"icon_home_selected","#86c751"));
arrayList.add(new FunctionItem("車主服務",false,"icon_home_selected","#86c751"));
arrayList.add(new FunctionItem("天天有料",false,"icon_home_selected","#86c751"));
TabItem tabItem = new TabItem("便民生活",arrayList);
tabItems.add(tabItem);
ArrayList<FunctionItem> arrayList1 = new ArrayList<>();
arrayList1.add(new FunctionItem("餘額寶",false,"icon_home_selected","#86c751"));
arrayList1.add(new FunctionItem("花唄",false,"icon_home_selected","#86c751"));
arrayList1.add(new FunctionItem("芝麻信用",false,"icon_home_selected","#86c751"));
arrayList1.add(new FunctionItem("螞蟻借唄",false,"icon_home_selected","#86c751"));
arrayList1.add(new FunctionItem("股票",false,"icon_home_selected","#86c751"));
arrayList1.add(new FunctionItem("保險服務",false,"icon_home_selected","#86c751"));
arrayList1.add(new FunctionItem("匯率換算",false,"icon_home_selected","#86c751"));
arrayList1.add(new FunctionItem("理財小工具",false,"icon_home_selected","#86c751"));
TabItem tabItem1 = new TabItem("財務管理",arrayList1);
tabItems.add(tabItem1);
ArrayList<FunctionItem> arrayList2 = new ArrayList<>();
arrayList2.add(new FunctionItem("轉賬",false,"icon_home_selected","#86c751"));
arrayList2.add(new FunctionItem("紅包",false,"icon_home_selected","#86c751"));
arrayList2.add(new FunctionItem("AA收款",false,"icon_home_selected","#86c751"));
arrayList2.add(new FunctionItem("親密付",false,"icon_home_selected","#86c751"));
arrayList2.add(new FunctionItem("上銀匯款",false,"icon_home_selected","#86c751"));
arrayList2.add(new FunctionItem("話費卡轉讓",false,"icon_home_selected","#86c751"));
TabItem tabItem2 = new TabItem("資金往來",arrayList2);
tabItems.add(tabItem2);
ArrayList<FunctionItem> arrayList3 = new ArrayList<>();
arrayList3.add(new FunctionItem("遊戲中心",false,"icon_home_selected","#86c751"));
arrayList3.add(new FunctionItem("出境",false,"icon_home_selected","#86c751"));
arrayList3.add(new FunctionItem("彩票",false,"icon_home_selected","#86c751"));
arrayList3.add(new FunctionItem("人臉識別",false,"icon_home_selected","#86c751"));
arrayList3.add(new FunctionItem("獎勵金",false,"icon_home_selected","#86c751"));
arrayList3.add(new FunctionItem("世界盃",false,"icon_home_selected","#86c751"));
TabItem tabItem3 = new TabItem("購物娛樂",arrayList3);
tabItems.add(tabItem3);
ArrayList<FunctionItem> arrayList4 = new ArrayList<>();
arrayList4.add(new FunctionItem("大學生活",false,"icon_home_selected","#86c751"));
arrayList4.add(new FunctionItem("愛心捐贈",false,"icon_home_selected","#86c751"));
arrayList4.add(new FunctionItem("螞蟻森林",false,"icon_home_selected","#86c751"));
arrayList4.add(new FunctionItem("螞蟻莊園",false,"icon_home_selected","#86c751"));
arrayList4.add(new FunctionItem("中小學",false,"icon_home_selected","#86c751"));
arrayList4.add(new FunctionItem("運動",false,"icon_home_selected","#86c751"));
TabItem tabItem4 = new TabItem("教育公益",arrayList4);
tabItems.add(tabItem4);
ArrayList<FunctionItem> arrayList5 = new ArrayList<>();
arrayList5.add(new FunctionItem("淘票票",false,"icon_home_selected","#86c751"));
arrayList5.add(new FunctionItem("滴滴出行",false,"icon_home_selected","#86c751"));
arrayList5.add(new FunctionItem("餓了麼外賣",false,"icon_home_selected","#86c751"));
arrayList5.add(new FunctionItem("天貓",false,"icon_home_selected","#86c751"));
arrayList5.add(new FunctionItem("淘寶",false,"icon_home_selected","#86c751"));
arrayList5.add(new FunctionItem("火車票機票",false,"icon_home_selected","#86c751"));
TabItem tabItem5 = new TabItem("第三方服務",arrayList5);
tabItems.add(tabItem5);
Gson gson = new Gson();
String json = gson.toJson(tabItems);
try {
FileOutputStream fos = new FileOutputStream("ceshi.xml");
fos.write(json.getBytes());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
有了測試資料之後,我將檔案複製到了Assert目錄下面。用來模擬資料請求。一般來說資料應該儲存在伺服器上。根據伺服器上請求的資料。去生成頁面。
第一個RecyclerView recyclerViewExist
recyclerViewExist.setLayoutManager(new GridLayoutManager(this, 4));
recyclerViewExist.setAdapter(blockAdapter);
recyclerViewExist.addItemDecoration(new SpaceItemDecoration(4, dip2px(this, 10)));
DefaultItemCallback callback = new DefaultItemCallback(blockAdapter);
DefaultItemTouchHelper helper = new DefaultItemTouchHelper(callback);
helper.attachToRecyclerView(recyclerViewExist);
blockAdapter主要是渲染一個item中圖片和文字,繫結點選的時候事件
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new ViewHolder(inflater.inflate(R.layout.layout_grid_item, parent, false));
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
final int index = position;
FunctionItem fi = data.get(position);
setImage(fi.imageUrl, holder.iv);
holder.text.setText(fi.name);
holder.btn.setImageResource(R.drawable.ic_block_delete);
holder.btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
FunctionItem fi = data.remove(index);
if (listener != null) {
listener.remove(fi);
}
notifyDataSetChanged();
}
});
}
通過DefaultItemCallback和DefaultItemTouchHelper實現了拖拽。核心程式碼
public DefaultItemCallback(ItemTouchHelperAdapter touchHelperAdapter) {
this.touchHelperAdapter = touchHelperAdapter;
}
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT; //允許上下左右的拖動
return makeMovementFlags(dragFlags, 0);
}
recyclerViewAll下面的所有的內容。也只是提供了一個點選的回撥和佈局渲染。
functionAdapter = new FunctionAdapter(this, allData);
recyclerViewAll.setLayoutManager(gridManager);
recyclerViewAll.setAdapter(functionAdapter);
SpaceItemDecoration spaceDecoration = new SpaceItemDecoration(4, dip2px(this, 10));
recyclerViewAll.addItemDecoration(spaceDecoration);
看一下中間horizonLScrollView 裡面寫了Radiogroup
向radiogoup新增radio.每次點選觸發一下回調函式
for (int i = 0; i < size; i++) {
FunctionItem item = tabs.get(i);
if(item.isTitle){
scrollTab.add(item.name);
RadioButton rb = new RadioButton(this);
rb.setPadding(padding, 0, padding, 0);
rb.setButtonDrawable(null);
rb.setGravity(Gravity.CENTER);
rb.setText(item.name);
rb.setTag(i);
rb.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
try {
rb.setTextColor(getResources().getColorStateList(R.color.bg_block_text));
} catch (Exception e) {
e.printStackTrace();
}
rb.setCompoundDrawablesWithIntrinsicBounds(null, null, null, getResources().getDrawable(R.drawable.bg_block_tab));
rb.setOnCheckedChangeListener(onCheckedChangeListener);
rg_tab.addView(rb);
}
}
當點選button的時候。根據上面rb.setTag(i)。這裡的i。表示選中的元素是所有列表的第幾個。每次點選的時候,呼叫下面的方法。移動到指定的位置
private void moveToPosition(int position) {
int first = gridManager.findFirstVisibleItemPosition();
int end = gridManager.findLastVisibleItemPosition();
if (first == -1 || end == -1)
return;
if (position <= first) { //移動到前面
gridManager.scrollToPosition(position);
} else if (position >= end) { //移動到後面
isMove = true;
scrollPosition = position;
gridManager.smoothScrollToPosition(recyclerViewAll, null, position);
} else {//中間部分
int n = position - gridManager.findFirstVisibleItemPosition();
if (n > 0 && n < allData.size()) {
int top = gridManager.findViewByPosition(position).getTop();
recyclerViewAll.scrollBy(0, top);
}
}
}
當RecyclerView滑動的時候。更新一下HorizontalScrollView重的RadioGroup。
recyclerViewAll.addOnScrollListener(onScrollListener); //新增一個滾動監聽
private RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
try {
if (isMove && newState == RecyclerView.SCROLL_STATE_IDLE) {
isMove = false;
View view = gridManager.findViewByPosition(scrollPosition);
if (view != null) {
int top = (int) view.getTop();
recyclerView.scrollBy(0, top);
}
}
if(newState==RecyclerView.SCROLL_STATE_DRAGGING){
isDrag = true;
}else{
isDrag = false;
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if(isDrag){ //拖動過程中
int position = gridManager.findFirstVisibleItemPosition();
if(position>0){
for(int i=0;i<position+1;i++){
if(allData.get(i).isTitle){
currentTab = allData.get(i).name;
}
}
scrollTab(currentTab);
}
}
}
};
private void scrollTab(String newTab) {
try {
int position = scrollTab.indexOf(currentTab);
int targetPosition = scrollTab.indexOf(newTab);
currentTab = newTab;
if (targetPosition != -1) {
int x = (targetPosition - position) * getTabWidth();
RadioButton radioButton = ((RadioButton) rg_tab.getChildAt(targetPosition));
radioButton.setOnCheckedChangeListener(null);
radioButton.setChecked(true);
radioButton.setOnCheckedChangeListener(onCheckedChangeListener);
horizonLScrollView.scrollBy(x, 0);
}
} catch (Exception e) {
e.printStackTrace();
}
}
至此所有的核心程式碼都在這裡了。其中整個工程分為兩個部分。一個app的activity的module。另一個是ceshi的lib。其中ceshi的lib只是用來生成對應的。