“忽见陌头杨柳色,悔教夫婿觅封侯”——(唐)王昌龄《闺怨》。一心只为追求美好生活的少妇忽然明白,与夫婿在一起享受春光才是最重要的。而为了未来的美好生活,失去当下的美好生活,这是古往今来人类最常见的懊悔。我们不仅要未雨绸缪,也要把握当下、珍惜现在。
一、ExpandableListView
最近用到了一个之前没用过的控件——ExpandableListView,从字面意思上就可以看出来,可以张开的列表控件。简单地总结一下。
因为ExpandableListView是Android自带的,因此不需要自己定义,直接在布局文件上写就行。比如:
<ExpandableListView
android:id="@+id/my_expandablelistview"
android:padding="5dp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
和其他的ListView一样,也需要为它提供数据,这里使用一个Map来存放数据,类型为<String, List<String>>:
private Map<String, List<String>> dataset = new HashMap<>();
private String[] parentList = new String[3];
初始化数据:
private void initialData() {
for(int i = 0; i < parentList.length; i++) {
parentList[i] = "i = " + i;
List<String> childrenList = new ArrayList<>();
childrenList.add("子项");
dataset.put(parentList[i], childrenList);
}
}
然后需要自己实现一个Adapte类,用于为ExpandableListView提供数据,该类继承了BaseExpandableListAdapter,下面这个是最简单的自定义的类:
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.TextView;
import java.util.List;
import java.util.Map;
/**
* Created by Data on 2019/3/14.
*/
public class ExpandableListViewAdapter extends BaseExpandableListAdapter {
private Map<String, List<String>> dataset;
private String[] parentList;
private Context context;
public ExpandableListViewAdapter(Map<String, List<String>> dataset, String[] parentList, Context context){
this.dataset = dataset;
this.parentList = parentList;
this.context = context;
}
// 获得某个父项的某个子项
@Override
public Object getChild(int parentPos, int childPos) {
return dataset.get(parentList[parentPos]).get(childPos);
}
// 获得父项的数量
@Override
public int getGroupCount() {
return dataset.size();
}
// 获得某个父项的子项数目
@Override
public int getChildrenCount(int parentPos) {
return dataset.get(parentList[parentPos]).size();
}
// 获得某个父项
@Override
public Object getGroup(int parentPos) {
return dataset.get(parentList[parentPos]);
}
// 获得某个父项的id
@Override
public long getGroupId(int parentPos) {
return parentPos;
}
// 获得某个父项的某个子项的id
@Override
public long getChildId(int parentPos, int childPos) {
return childPos;
}
// 按函数的名字来理解应该是是否具有稳定的id,这个方法目前一直都是返回false,没有去改动过
@Override
public boolean hasStableIds() {
return false;
}
// 获得父项显示的view
@Override
public View getGroupView(int parentPos, boolean b, View view, ViewGroup viewGroup) {
if (view == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.item_parent, null);
}
view.setTag(R.layout.item_parent, parentPos);
view.setTag(R.layout.item_child, -1);
TextView text = (TextView) view.findViewById(R.id.parent_title);
text.setText(parentList[parentPos]);
return view;
}
// 获得子项显示的view
@Override
public View getChildView(int parentPos, int childPos, boolean b, View view, ViewGroup viewGroup) {
if (view == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.item_child, null);
}
view.setTag(R.layout.item_parent, parentPos);
view.setTag(R.layout.item_child, childPos);
TextView text = (TextView) view.findViewById(R.id.child_title);
text.setText(dataset.get(parentList[parentPos]).get(childPos));
return view;
}
// 子项是否可选中,如果需要设置子项的点击事件,需要返回true
@Override
public boolean isChildSelectable(int i, int i1) {
return true;
}
}
其中的item_parent和item_child分别是父项和子项的布局文件。值得注意的是父项最左侧的箭头是自带的,不需要自己设置,如果要修改可以通过android:groupIndicator=""或者mExLv.setGroupIndicator();,设置为null时,就是去掉箭头了。
设置点击事件。首先需要将自定义的adapter中的isChildSelectable方法的返回值设置为true(否则子项的点击不生效),之后调用ExpandableListView的setOnChildClickListener方法即可:
listview.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
@Override
public boolean onChildClick(ExpandableListView expandableListView, View view, int parentPos, int childPos, long l) {
Toast.makeText(ExpandableListViewTestActivity.this, dataset.get(parentList[parentPos]).get(childPos), Toast.LENGTH_SHORT).show();
return true;
}
});
另外,也可以对父项和子项中的某个控件设置监听点击事件。
ExpandableListView中关于长按有一个setOnItemLongClickListener()方法,但是这个方法有个问题,没办法区分被长按的item是父项还是子项,所以需要在自定义adapter的getGroupView方法和getChildView方法中添加几句代码来区分是父项还是子项。具体的见上面Adapter的代码。
view.setTag(R.layout.parent_item, parentPos);
view.setTag(R.layout.child_item, -1);
这里用到了view的setTag方法,一共设置了两个Tag,标签虽然在设置的时候提示说只要int类型即可,但一开始使用0和1来做tag的时候,显示没有报错,但编译运行就报错了,要求是资源文件的id才行,因此换成了R.layout.parent_item和R.layout.child_item。如果是父项,就设置R.layout.parent_item为第几个父项,设置R.layout.child_item为-1。如果是子项,就设置R.layout.parent_item属于第几个父项,设置R.layout.child_item为该父项的第几个子项,这样就可以区分被长按的是父项还是子项了。然后设置ExpandableListView长按item的监听器:
listview.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) {
String content = "";
if ((int) view.getTag(R.layout.child_item) == -1) {
content = "父类第" + view.getTag(R.layout.parent_item) + "项" + "被长按了";
} else {
content = "父类第" + view.getTag(R.layout.parent_item) + "项" + "中的"
+ "子类第" + view.getTag(R.layout.child_item) + "项" + "被长按了";
}
Toast.makeText(ExpandableListViewTestActivity.this, content, Toast.LENGTH_SHORT).show();
return true;
}
});
另外,如果需要更新数据的话,将数据准备好,再调用adapter.notifyDataSetChanged()即可!
二、Zip压缩
这个就没有什么好说的了,直接上代码!
package com.zipUtil;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
public class ZipUtil {
public static void zip(String src, String dest) throws IOException {
// 提供了一个数据项压缩成一个ZIP归档输出流
ZipOutputStream out = null;
try {
File outFile = new File(dest);// 源文件或者目录
File fileOrDirectory = new File(src);// 压缩文件路径
out = new ZipOutputStream(new FileOutputStream(outFile));
// 如果此文件是一个文件,否则为false。
if (fileOrDirectory.isFile()) {
zipFileOrDirectory(out, fileOrDirectory, "");
} else {
// 返回一个文件或空阵列。
File[] entries = fileOrDirectory.listFiles();
for (int i = 0; i < entries.length; i++) {
// 递归压缩,更新curPaths
zipFileOrDirectory(out, entries[i], "");
}
}
} catch (IOException ex) {
ex.printStackTrace();
} finally {
// 关闭输出流
if (out != null) {
try {
out.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
private static void zipFileOrDirectory(ZipOutputStream out, File fileOrDirectory, String curPath)
throws IOException {
// 从文件中读取字节的输入流
FileInputStream in = null;
try {
// 如果此文件是一个目录,否则返回false。
if (!fileOrDirectory.isDirectory()) {
// 压缩文件
byte[] buffer = new byte[4096];
int bytes_read;
in = new FileInputStream(fileOrDirectory);
// 实例代表一个条目内的ZIP归档
ZipEntry entry = new ZipEntry(curPath + fileOrDirectory.getName());
// 条目的信息写入底层流
out.putNextEntry(entry);
while ((bytes_read = in.read(buffer)) != -1) {
out.write(buffer, 0, bytes_read);
}
out.closeEntry();
} else {
// 压缩目录
File[] entries = fileOrDirectory.listFiles();
for (int i = 0; i < entries.length; i++) {
// 递归压缩,更新curPaths
zipFileOrDirectory(out, entries[i], curPath + fileOrDirectory.getName() + "/");
}
}
} catch (IOException ex) {
ex.printStackTrace();
// throw ex;
} finally {
if (in != null) {
try {
in.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
@SuppressWarnings("unchecked")
public static void unzip(String zipFileName, String outputDirectory) throws IOException {
ZipFile zipFile = null;
try {
zipFile = new ZipFile(zipFileName);
Enumeration e = zipFile.entries();
ZipEntry zipEntry = null;
File dest = new File(outputDirectory);
dest.mkdirs();
while (e.hasMoreElements()) {
zipEntry = (ZipEntry) e.nextElement();
String entryName = zipEntry.getName();
InputStream in = null;
FileOutputStream out = null;
try {
if (zipEntry.isDirectory()) {
String name = zipEntry.getName();
name = name.substring(0, name.length() - 1);
File f = new File(outputDirectory + File.separator + name);
f.mkdirs();
} else {
int index = entryName.lastIndexOf("\\");
if (index != -1) {
File df = new File(outputDirectory + File.separator + entryName.substring(0, index));
df.mkdirs();
}
index = entryName.lastIndexOf("/");
if (index != -1) {
File df = new File(outputDirectory + File.separator + entryName.substring(0, index));
df.mkdirs();
}
File f = new File(outputDirectory + File.separator + zipEntry.getName());
// f.createNewFile();
in = zipFile.getInputStream(zipEntry);
out = new FileOutputStream(f);
int c;
byte[] by = new byte[1024];
while ((c = in.read(by)) != -1) {
out.write(by, 0, c);
}
out.flush();
}
} catch (IOException ex) {
ex.printStackTrace();
throw new IOException("解压失败:" + ex.toString());
} finally {
if (in != null) {
try {
in.close();
} catch (IOException ex) {
}
}
if (out != null) {
try {
out.close();
} catch (IOException ex) {
}
}
}
}
} catch (IOException ex) {
ex.printStackTrace();
throw new IOException("解压失败:" + ex.toString());
} finally {
if (zipFile != null) {
try {
zipFile.close();
} catch (IOException ex) {
}
}
}
}
}
调用:
ZipUtil.zip(Environment.getExternalStorageDirectory().getPath()+"/MLGJing", Environment.getExternalStorageDirectory().getPath()+"/MLGJing.zip");
ZipUtil.unzip(Environment.getExternalStorageDirectory().getPath()+"/MLGJing.zip", Environment.getExternalStorageDirectory().getPath());
三、地图城市切换
地图城市切换,说到底就是地图控件的中心点移动,并设置相应的显示级别,不管是基于百度地图开发还是基于高德地图开发,方法都大同小异,也比较简单。主要还是没有合适的城市经纬度数据。因此给大家推荐一波数据:全国省市经纬度、地图显示级别,它里面包含全国34个省级行政单位以及所有地级市的名称和经纬度,包括各个直辖市、省、自治区、香港、澳门特别行政区和台湾地区。格式为JSON字符串,使用fastjson等可以直接解析。当然了,没有积分的同志们可以直接在评论区留下邮箱哈~~
今天就写到这里吧,希望下周能学到更多的东西,有更大的进步!
参考:
1、https://blog.csdn.net/sysukehan/article/details/51960473