在做项目时常常会遇到选择本地图片的需求。曾经都是懒得写直接调用系统方法来选择图片。可是这样并不能实现多选效果。近期又遇到了,所以还是写一个demo好了。以后也方便使用。还是首先来看看效果

显示的图片使用RecyclerView实现的,利用Glide来载入;以下弹出的图片目录效果是採用PopupWindow实现,这里比採用PopupWindow更方便,弹出显示的左边图片是这个目录里的第一张图片;选中的图片能够进行预览,使用网上一个大神写的来实现的;至于图片的获取是用ContentProvider。

看看主界面的布局文件。上面一栏是一个返回button和一个跳转预览界面的button。依据是否有选中的图片来设置它的点击和显示状态。中间就是一个用于显示图片的RecyclerView,左下角是显示目录的名字可点击切换。右下角就是确定button。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.cdxsc.imageselect_y.ImageSelecteActivity"> <RelativeLayout
android:layout_width="match_parent"
android:layout_height="45dp"
android:background="@android:color/white"> <ImageButton
android:id="@+id/ib_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="10dp"
android:background="@mipmap/action_bar_back_normal" /> <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@id/ib_back"
android:text="选择图片"
android:textColor="#000"
android:textSize="16sp" /> <TextView
android:id="@+id/tv_preview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="10dp"
android:enabled="false"
android:text="预览"
android:textColor="#BEBFBF"
android:textSize="16sp" />
</RelativeLayout> <View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="#eeeeee" /> <android.support.v7.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"></android.support.v7.widget.RecyclerView> <RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp"> <TextView
android:id="@+id/tv_allPic"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_marginLeft="10dp"
android:clickable="true"
android:gravity="center_vertical"
android:text="全部图片"
android:textColor="@android:color/black"
android:textSize="16sp" /> <Button
android:id="@+id/bt_confirm"
android:layout_width="wrap_content"
android:layout_height="35dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="10dp"
android:background="@drawable/shape_disable"
android:enabled="false"
android:text="确定"
android:textColor="#676767"
android:textSize="16sp" />
</RelativeLayout>
</LinearLayout>

好了。如今看主界面的代码

public class ImageSelecteActivity extends AppCompatActivity {

    private static final String TAG = "lzy";
@BindView(R.id.ib_back)
ImageButton mButtonBack;
@BindView(R.id.tv_preview)
TextView mTextViewPreview;
@BindView(R.id.rv)
RecyclerView mRecyclerView;
@BindView(R.id.tv_allPic)
TextView mTextViewAllPic;
@BindView(R.id.bt_confirm)
Button mButtonConfirm;
private GalleryPopupWindow mPopupWindow;
//存储每一个目录下的图片路径,key是文件名称
private Map<String, List<String>> mGroupMap = new HashMap<>();
private List<ImageBean> list = new ArrayList<>();
//当前目录显示的图片路径
private List<String> listPath = new ArrayList<>();
//所选择的图片路径集合
private ArrayList<String> listSelectedPath = new ArrayList<>(); private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
//扫描完毕后
getGalleryList();
listPath.clear();
listPath.addAll(mGroupMap.get("全部图片"));
adapter.update(listPath);
if (mPopupWindow != null)
mPopupWindow.notifyDataChanged();
}
};
private ImageSelectAdapter adapter; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_image_selecte);
ButterKnife.bind(this);
init();
} private void init() {
getImages();
mRecyclerView.setLayoutManager(new GridLayoutManager(ImageSelecteActivity.this, 3));
adapter = new ImageSelectAdapter(this, listPath);
mRecyclerView.setAdapter(adapter);
adapter.setOnCheckedChangedListener(onCheckedChangedListener);
} @OnClick({R.id.ib_back, R.id.tv_preview, R.id.tv_allPic, R.id.bt_confirm})
public void onClick(View view) {
switch (view.getId()) {
case R.id.ib_back:
finish();
break;
case R.id.tv_preview://跳转预览界面
Intent intent = new Intent(ImageSelecteActivity.this, ImagePreviewActivity.class);
//把选中的图片集合传入预览界面
intent.putStringArrayListExtra("pic", listSelectedPath);
startActivity(intent);
break;
case R.id.tv_allPic://选择图片目录
if (mPopupWindow == null) {
//把目录列表的集合传入显示
mPopupWindow = new GalleryPopupWindow(this, list);
mPopupWindow.setOnItemClickListener(new GalleryPopupWindow.OnItemClickListener() {
@Override
public void onItemClick(String fileName) {
//切换了目录。清除之前的选择的信息
setButtonDisable();
listPath.clear();
listSelectedPath.clear();
//把当前选择的目录内图片的路径放入listPath,更新界面
listPath.addAll(mGroupMap.get(fileName));
adapter.update(listPath);
mTextViewAllPic.setText(fileName);
}
});
}
mPopupWindow.showAtLocation(mRecyclerView, Gravity.BOTTOM, 0, dp2px(50, ImageSelecteActivity.this));
break;
case R.id.bt_confirm://确定
for (int i = 0; i < listSelectedPath.size(); i++) {
//这里可通过Glide把它转为Bitmap
Glide.with(this).load("file://" + listSelectedPath.get(i)).asBitmap().into(new SimpleTarget<Bitmap>() {
@Override
public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
Log.i(TAG, "onResourceReady: " + resource);
}
});
}
break;
}
} /**
* dp转px
*/
public static int dp2px(int dp, Context context) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
context.getResources().getDisplayMetrics());
} //选择图片变化的监听
private ImageSelectAdapter.OnCheckedChangedListener onCheckedChangedListener = new ImageSelectAdapter.OnCheckedChangedListener() {
@Override
public void onChanged(boolean isChecked, String path, CheckBox cb, int position) {
if (isChecked) {//选中
if (listSelectedPath.size() == 9) {
Toast.makeText(ImageSelecteActivity.this, "最多选择9张图片", Toast.LENGTH_SHORT).show();
//把点击变为checked的图片变为没有checked
cb.setChecked(false);
adapter.setCheckedBoxFalse(position);
return;
}
//选中的图片路径加入集合
listSelectedPath.add(path); } else {//取消选中
//从集合中移除
if (listSelectedPath.contains(path))
listSelectedPath.remove(path);
}
//假设没有选中的button不可点击
if (listSelectedPath.size() == 0) {
setButtonDisable();
} else {
setButtonEnable();
}
}
}; //选中图片时的button状态
private void setButtonEnable() {
mButtonConfirm.setBackgroundResource(R.drawable.selector_bt);
mButtonConfirm.setTextColor(Color.parseColor("#ffffff"));
mButtonConfirm.setEnabled(true);
mTextViewPreview.setEnabled(true);
mTextViewPreview.setTextColor(getResources().getColor(R.color.colorAccent));
mButtonConfirm.setText("确定" + listSelectedPath.size() + "/9");
} //没有选择时button状态
private void setButtonDisable() {
mButtonConfirm.setBackgroundResource(R.drawable.shape_disable);
mButtonConfirm.setTextColor(Color.parseColor("#676767"));
mButtonConfirm.setEnabled(false);
mTextViewPreview.setEnabled(false);
mTextViewPreview.setTextColor(Color.parseColor("#BEBFBF"));
mButtonConfirm.setText("确定");
} /**
* 利用ContentProvider扫描手机中的图片。此方法在执行在子线程中
*/
private void getImages() {
new Thread(new Runnable() { @Override
public void run() {
Uri mImageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
ContentResolver mContentResolver = ImageSelecteActivity.this.getContentResolver();
//仅仅查询jpeg和png的图片
// Cursor mCursor = mContentResolver.query(mImageUri, null,
// MediaStore.Images.Media.MIME_TYPE + "=? or "
// + MediaStore.Images.Media.MIME_TYPE + "=? or " + MediaStore.Images.Media.MIME_TYPE + "=?",
// new String[]{"image/jpeg", "image/png", "image/jpg"}, MediaStore.Images.Media.DATE_MODIFIED);
Cursor mCursor = mContentResolver.query(mImageUri, null, null, null,
MediaStore.Images.Media.DATE_MODIFIED);
if (mCursor == null) {
return;
}
//存放全部图片的路径
List<String> listAllPic = new ArrayList<String>();
while (mCursor.moveToNext()) {
//获取图片的路径
String path = mCursor.getString(mCursor
.getColumnIndex(MediaStore.Images.Media.DATA)); //获取该图片的父路径名
String parentName = new File(path).getParentFile().getName();
listAllPic.add(path); //依据父路径名将图片放入到mGruopMap中
if (!mGroupMap.containsKey(parentName)) {
List<String> chileList = new ArrayList<String>();
chileList.add(path);
mGroupMap.put(parentName, chileList);
} else {
mGroupMap.get(parentName).add(path);
}
}
//加入全部图片
mGroupMap.put("全部图片", listAllPic);
//通知Handler扫描图片完毕
mHandler.sendEmptyMessage(0);
mCursor.close();
}
}).start(); } //获取相冊目录列表
private void getGalleryList() {
Iterator<Map.Entry<String, List<String>>> iterator = mGroupMap.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, List<String>> next = iterator.next();
ImageBean imageBean = new ImageBean();
imageBean.setFileName(next.getKey());
imageBean.setFirstPicPath(next.getValue().get(0));
imageBean.setCount(next.getValue().size());
if (next.getKey().equals("全部图片"))
list.add(0, imageBean);
else
list.add(imageBean);
}
}
}

·mGroupMap:这个是以目录名为key,目录内的图片路径集合为value,也就是依照目录来分别存储了全部图片的路径。

·listPath:保存的是当前显示在界面上的目录内的图片路径集合

·listSelectedPath:保存用户选中的图片路径

·list:保存的是ImageBean的集合。ImageBean保存了目录名、里面首张图片的路径以及里面所包括图片的数量,当切换目录时用于显示

·getImages():这种方法就是用来扫描手机里图片并保存的,这是在子线程中执行的,显示这可能是一个耗时的任务。通过ContentProvider获取到一个包括全部图片的Cursor,然后遍历这个Cursor把所需的数据就保存在mGroupMap里面,最后利用Handler通知界面更新。
·getGalleryList():这种方法就是mGroupMap里面的数据来给list赋值,也就是产生一个现实目录列表所需的数据集合。

·GalleryPopupWindow也就是我们用于显示文件列表的,在67--84行就是一些GalleryPopupWindow的设置,调用showAtLocation方法把PopupWindow显示在距离底部50dp的位置,并设置了点击的回调,当切换了一个目录后要做的相关操作就在这里进行。GalleryPopupWindow再待会再详细看看

接下来再看看中间RecyclerView的Adapter

public class ImageSelectAdapter extends RecyclerView.Adapter<ImageSelectAdapter.NViewHolder> {

    private Context context;
private List<String> list = new ArrayList<>();
private OnCheckedChangedListener onCheckedChangedListener;
private List<Boolean> listChecked = new ArrayList<>(); public ImageSelectAdapter(Context context, List<String> list) {
this.context = context;
this.list.addAll(list);
setListCheched(list);
} public void update(List<String> list) {
this.list.clear();
this.list.addAll(list);
setListCheched(list);
notifyDataSetChanged(); } /**
* 设置listChecked的初始值
*
* @param list
*/
private void setListCheched(List<String> list) {
listChecked.clear();
for (int i = 0; i < list.size(); i++) {
listChecked.add(false);
}
} //当点击超过了九张图片,再点击的设置为false
public void setCheckedBoxFalse(int pos) {
listChecked.set(pos, false);
} public interface OnCheckedChangedListener {
/**
* @param isChecked 是否选中
* @param path 点击的图片路径
* @param cb 点击的CheckBox
* @param pos 点击的位置
*/
void onChanged(boolean isChecked, String path, CheckBox cb, int pos);
} public void setOnCheckedChangedListener(OnCheckedChangedListener onCheckedChangedListener) {
this.onCheckedChangedListener = onCheckedChangedListener;
} @Override
public NViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new NViewHolder(LayoutInflater.from(context).inflate(R.layout.item_image_select, parent, false));
} @Override
public void onBindViewHolder(final NViewHolder holder, final int position) {
Glide.with(context).load("file://" + list.get(position)).into(holder.iv);
holder.cb.setChecked(listChecked.get(position));
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
holder.cb.setChecked(!holder.cb.isChecked());
if (holder.cb.isChecked()) {
listChecked.set(position, true);
} else {
listChecked.set(position, false);
}
if (onCheckedChangedListener != null) {
onCheckedChangedListener.onChanged(holder.cb.isChecked(), list.get(position), holder.cb, position);
}
}
});
} @Override
public int getItemCount() {
return list.size();
} public class NViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.iv_itemImageSelect)
ImageView iv;
@BindView(R.id.cb_itemImageSelect)
CheckBox cb; public NViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
} }

这里Item的布局文件就是一个ImageView加一个CheckBox。依据选中状态改变CheckBox的状态,这里就不贴出来了。

·listChecked:这个集合是用来存储每一个位置是否Check的,假设在onBindViewHolder里面不设置CheckBox的状态的话,由于复用问题会出问题,所以想出了用一个集合来保存它们状态的方法,不知道大家有没有其它更好的方法。

·OnCheckedChangedListener:向外暴露的接口,把点击的位置等參数都传到Activity中去。

·update():这种方法用来更新界面的,没有採用直接调notifyDataSetChanged方法是由于,假设数据的数量变化了那么listChecked的数量也要发生变化才行这样才干相应。所以写了这种方法。

再接着看看GalleryPopupWindow

/**
* Created by lzy on 2017/2/8.
*/
public class GalleryPopupWindow extends PopupWindow {
private static final String TAG = "lzy"; RecyclerView mRecyclerView; private Activity activity;
private GalleryPopupWindow.OnItemClickListener onItemClickListener;
private List<ImageBean> list;
private GalleryAdapter adapter; public GalleryPopupWindow(Activity context, List<ImageBean> list) {
super(context);
this.activity = context;
this.list = list;
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View contentView = inflater.inflate(R.layout.popu_gallery, null);
initView(contentView); int h = context.getWindowManager().getDefaultDisplay().getHeight();
int w = context.getWindowManager().getDefaultDisplay().getWidth();
this.setContentView(contentView);
this.setWidth(w);
this.setHeight(ImageSelecteActivity.dp2px(350, context));
this.setFocusable(false);
this.setOutsideTouchable(true);
this.update(); setBackgroundDrawable(new ColorDrawable(000000000));
} public void notifyDataChanged() {
adapter.notifyDataSetChanged();
} private void initView(View contentView) {
mRecyclerView = (RecyclerView) contentView.findViewById(R.id.rv_gallery);
mRecyclerView.setLayoutManager(new LinearLayoutManager(activity));
adapter = new GalleryAdapter(list, activity);
adapter.setOnItemClickListener(new GalleryAdapter.OnItemClickListener() {
@Override
public void onItemClick(String fileName) {
if (onItemClickListener != null) {
onItemClickListener.onItemClick(fileName);
dismiss();
}
}
});
mRecyclerView.setAdapter(adapter); } //暴露点击的接口
public interface OnItemClickListener {
/**
* @param keyValue
*/
void onItemClick(String keyValue);
} public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
}

这个PopupWindow的布局文件就是一个RecyclerView,所以这里面也没什么。也就是设置RecyclerView。然后向外暴露一个点击的接口,用于Activity接收是点击了哪个目录,所以接口參数也就是目录名,再看看这个PopupWindow的Adapter

/**
* Created by lzy on 2017/2/8.
*/
public class GalleryAdapter extends RecyclerView.Adapter<GalleryAdapter.NViewHolder> { private Context context;
private List<ImageBean> list;
private OnItemClickListener onItemClickListener;
//用于记录是选中的哪一个目录
private int selectedPos; public GalleryAdapter(List<ImageBean> list, Context context) {
this.list = list;
this.context = context;
} public interface OnItemClickListener {
void onItemClick(String fileName);
} public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
} @Override
public NViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new NViewHolder(LayoutInflater.from(context).inflate(R.layout.item_gallery, parent, false));
} @Override
public void onBindViewHolder(NViewHolder holder, final int position) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
selectedPos = position;
notifyDataSetChanged();
if (onItemClickListener != null) {
onItemClickListener.onItemClick(list.get(position).getFileName());
}
}
});
if (position == selectedPos) {
holder.ivCheck.setVisibility(View.VISIBLE);
} else {
holder.ivCheck.setVisibility(View.GONE);
}
holder.tvCount.setText(list.get(position).getCount() + "张");
holder.tvName.setText(list.get(position).getFileName());
Glide.with(context).load("file://" + list.get(position).getFirstPicPath()).into(holder.iv);
} @Override
public int getItemCount() {
return list.size();
} public class NViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.iv_itemGallery)
ImageView iv;
@BindView(R.id.tv_itemGallery_name)
TextView tvName;
@BindView(R.id.tv_itemGallery_count)
TextView tvCount;
@BindView(R.id.iv_itemGallery_check)
ImageView ivCheck; public NViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
} }

这里有个接口是把点击的文件名称传递给PopupWindow。然后再给Activity。selectedPos是用来记录选择的是哪一个目录。显示相应的CheckBox。

这里就几乎相同完毕了,感兴趣的能够下载Demo来看看。

再说一下,这里显示图片都是採用的Glide。使用也非常方便,我们获取的图片路径都是文件路径。假设要转化为Bitmap也能够直接调用Glide的方法就能够轻松实现,例如以下所看到的:

Glide.with(this).load("file://" + listSelectedPath.get(i)).asBitmap().into(new SimpleTarget<Bitmap>() {
@Override
public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
Log.i(TAG, "onResourceReady: " + resource);
}
});

当中找寻控件都没有使用findViewById,而是採用的ButterKnife。节约了大量的时间,顺便说说导入的方法

在app以下的build.gradle中加入以下:

apply plugin: 'com.neenbedankt.android-apt'
apt 'com.jakewharton:butterknife-compiler:8.1.0'
compile 'com.github.bumptech.glide:glide:3.5.2'

项目以下的build.gradle

 //加入apt插件
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

加入插件

File->Setting->Plugins  搜索zelezny。例如以下所看到的

当须要使用的时候。直接在光标移动到布局文件,点击Alt+Insert。选择Generate ButterKnife Injections

就出现例如以下界面,能够自己主动生成了

源代码地址:http://download.csdn.net/detail/lylodyf/9768761

Android实现本地图片选择及预览缩放效果仿春雨医生的更多相关文章

  1. JavaScript实现本地图片上传预览功能(兼容IE、chrome、FF)

    需要解决的问题有:本地图片如何在上传前预览.编辑:最近发现这个功能很多是基于flash实现的,很多JavaScript实现的代码兼容性都很差,特别是在IE和firefox和chrome三个浏览器上不兼 ...

  2. 微信小程序图片选择,预览和删除

    这里均用的是小程序原生api 废话不多说直接上栗子: <view class="addImv"> <!--这个是已经选好的图片--> <view wx ...

  3. jquery实现本地图片上传预览和限流处理

    <html> <head> <meta http-equiv="Content-Type" content="text/html; char ...

  4. JS实现上传本地图片前先预览

    <style type="text/css"> #preview /*这个就是预览的DIV的ID*/ { filter:progid:DXImageTransform. ...

  5. HTML5实现图片选择并预览

    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs& ...

  6. Html5选择图片并及时预览图片

    以往想要实现图片预览基本都是先传至服务器后等返回链接地址才能进行预览,使用Html5选择图片并及时预览图片的代码如下,使用起来更爽了. <!DOCTYPE html> <html l ...

  7. input type=file 选择图片并且实现预览效果的实例

    为大家带来一篇input type=file 选择图片并且实现预览效果的实例. 通过<input />标签,给它指定type类型为file,可提供文件上传: accept:可选择上传类型, ...

  8. vue使用readAsDataURL实现选择图片文件后预览

    vue实现选择图片文件后预览 利用h5的api可以实现选择文件并实现预览 readAsDataURL 方法会读取指定的 Blob 或 File 对象.读取操作完成的时候,readyState 会变成已 ...

  9. js实现图片上传预览及进度条

    原文js实现图片上传预览及进度条 最近在做图片上传的时候,由于产品设计的比较fashion,上网找了比较久还没有现成的,因此自己做了一个,实现的功能如下: 1:去除浏览器<input type= ...

随机推荐

  1. 构造函数的理解(构造函数与 init 方法)

    0. 构造函数与 init 方法 构造方法内部禁止添加任何业务逻辑,如果有业务逻辑,请放在 init 方法中: 1. 构造函数的参数 以下为一个堆实现优先队列(堆的实现借助完全二叉树,而完全二叉树又可 ...

  2. 图论之堆优化的Prim

    本题模板,最小生成树,洛谷P3366 题目描述 如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出orz 输入输出格式 输入格式: 第一行包含两个整数N.M,表示该图共有N个结点和M条无向边 ...

  3. feign client传递对象

    http://bbs.springcloud.cn/d/134-feign-client server端申明 @RestController public class HelloController ...

  4. JavaScript:理解prototype与__proto__,原型与原型链

    JS中的继承是原型继承,通过原型实现的.为了理解原型,我想先讲讲对象的内部属性[[prototype]]和属性__proto__,函数的属性prototype. 对象的内部属性[[prototype] ...

  5. [POI2008]POD-Subdivision of Kingdom(搜索+状压)

    题意 给定一个n个点的无向图,要求将点集分成大小相等的两个子集,使两个子集之间的边数最少 (n<=26) 题解 一开始想了半天DP发现不会,去看题解全是搜索. 所以发现C(1326)可以过我就写 ...

  6. 题解 P2068 【统计和】

    这是一道单点修改,区间查询的线段树. 需要实现的操作有三个:建树,更新与查询. 首先,线段树用结构体维护,如下: struct node { int l, r; int val; } tree[max ...

  7. 在iPad iOS8环境下打开相冊或者拍照

    在iPad下打开相冊或者拍照,假设使用 UIImagePickerController 打开相冊或者拍照,那必需要用到 UIPopoverController 去打开. UIPopoverContro ...

  8. iOS开发 - 数据归档与恢复 NSKeyedArchiver

    归档与恢复归档 归档,英文Archiver['ɑrkɪvə],这里指的是将OC的对象存储为一个文件或者网络上的一个数据块. 恢复归档.英文UnArchiver,指的是将一个来自文件或网络的归档数据块恢 ...

  9. Android禁止ViewPager的左右滑动

    转载请注明出处:http://blog.csdn.net/allen315410/article/details/40744287 有时候在开发中会遇到一些"诡异"的要求,比方在V ...

  10. Ubuntu系统下的Mysql安装与使用

    摘要 在本篇博文中.笔者将从基础出发.介绍Mysql在Linux环境下的安装和基本使用命令,仅适用于Mysql刚開始学习的人.大牛请绕道-- 安装Mysql数据库 这里介绍最最简单的安装方式,至于编译 ...