

整体介绍
总的来说实现了从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只是用来生成对应的。