如果你想让用户在使用应用时,只要通过一个手指放在一个UITableViewCell上,就能对其进行剪切、拷贝、 粘贴的操作,那么你需要实现UITableViewDelegate协议中的以下三个方法:
1、- (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath
如果返回YES则会显示快捷菜单
2、- (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender
一旦你允许UITableViewCell显示快捷菜单,那么iOS将会多次调用这个方法,并且通过你对选择器的操作来决定是不是要显示快捷菜单。所以如果iOS问你是否想要对用户显示拷贝菜单,这个方法就会在UITableView的委托对象中调用,这个方法的 canPerformAction参数等同于@selector(copy:)
3、- (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender
在UITableViewCell的快捷菜单中,一旦你允许显示特定动作,并且一旦用户从菜单中选中了这个动作时,这个方法会在UITableView的委托对象中调用。此时,你必须采取任何满足用户需求的行动。例如,如果用户选择的拷贝,你将需要有一个粘贴板用来放那些被选中的UITableView单元格的内容。
UITableView会给iOS一个是/否的回答,允许或者不允许它为UITableViewCell显示可用系统菜单选项。当用户把手指放在UITableViewCell上一个确定的时间内,严格来说大约1秒钟,iOS试图在UITableViewCell上显示一个快捷菜单。随后 iOS会询问UITableView,UITableViewCell是菜单触发的来源。如果UITableView给了iOS一个肯定的回答,那么iOS随后会告诉UITableView什么选项适合显示于快捷菜单中,UITableView才能够对任一选项说是或者否。如果有5个适用于实例的菜单选项,UITableView只对其中的2个说是,那么只有2个选项显示出来。在菜单选项显示给用户之后,用户既能点击其中一个项目或者点击快捷菜单的外围来取消点击效果。一旦用户点击了其中一个菜单选项,iOS将发送一条委托消息到UITableView,通知它用户点击的菜单选项。基于所有这些信息,UITableView可以做一个决定,要对这些已经选择的动作做什么。
- (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath { return YES; } - (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender { return YES; } - (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender { if (action == @selector(copy:)) { [UIPasteboard generalPasteboard].string = [dataArray objectAtIndex:indexPath.row]; } if (action == @selector(cut:)) { [UIPasteboard generalPasteboard].string = [dataArray objectAtIndex:indexPath.row]; [dataArray replaceObjectAtIndex:indexPath.row withObject:@""]; [tbl reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone]; } if (action == @selector(paste:)) { NSString *pasteString = [UIPasteboard generalPasteboard].string; NSString *tmpString = [NSString stringWithFormat:@"%@ %@", [dataArray objectAtIndex:indexPath.row], pasteString]; [dataArray replaceObjectAtIndex:indexPath.row withObject:tmpString]; [tbl reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone]; } }
可以支持的菜单列表如下:
cut:
copy:
select:
selectAll:
paste:
delete:
_promptForReplace:
_showTextStyleOptions:
_define:
_addShortcut:
_accessibilitySpeak:
_accessibilitySpeakLanguageSelection:
_accessibilityPauseSpeaking:
makeTextWritingDirectionRightToLeft:
makeTextWritingDirectionLeftToRight:
直接上代码:
import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import android.app.AlertDialog; import android.app.AlertDialog.Builder; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.net.Uri; import android.os.Environment; import android.os.Handler; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.widget.ProgressBar; import com.iblueyeClient.R; import com.iblueyeClient.utils.Msg; public class UpdateManager { private Context mContext; // 返回的安装包url private String apkUrl = "http://www.iblueye.com/client/download/ibePhone.apk"; private Dialog noticeDialog; private Dialog downloadDialog; /* 下载包安装路径 */ private static final String savePath = Environment .getExternalStorageDirectory().getPath(); private static final String saveFileName = savePath + "/" + "ibePhone.apk"; private Thread downLoadThread; private boolean interceptFlag = false; public ProgressBar mProgress;/* 进度条与通知ui刷新的handler和msg常量 */ public int progress; private Handler handler; public UpdateManager(Context context, Handler h) { this.mContext = context; handler = h; } // 外部接口让主Activity调用 public void confirmUpdate() { AlertDialog.Builder builder = new Builder(mContext); builder.setTitle(R.string.update_version); builder.setMessage(R.string.update_msg); builder.setPositiveButton(R.string.download, new OnClickListener() { public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); showDownloadDialog(); } }); builder.setNegativeButton(R.string.next_download, new OnClickListener() { public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); handler.sendEmptyMessage(Msg.START); } }); noticeDialog = builder.create(); noticeDialog.show(); } private void showDownloadDialog() { final LayoutInflater inflater = LayoutInflater.from(mContext); View v = inflater.inflate(R.layout.progress, null); mProgress = (ProgressBar) v.findViewById(R.id.progress); AlertDialog.Builder builder = new Builder(mContext); builder.setTitle(R.string.update_version); builder.setView(v); builder.setNegativeButton(R.string.cancel, new OnClickListener() { public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); interceptFlag = true; handler.sendEmptyMessage(Msg.START); } }); downloadDialog = builder.create(); downloadDialog.show(); downloadApk(); } private Runnable mdownApkRunnable = new Runnable() { public void run() { try { URL url = new URL(/blog_article/apkUrl/index.html); HttpURLConnection conn = (HttpURLConnection) url .openConnection(); conn.connect(); int length = conn.getContentLength(); InputStream is = conn.getInputStream(); File file = new File(savePath); if (!file.exists()) { file.mkdir(); } String apkFile = saveFileName; File ApkFile = new File(apkFile); FileOutputStream fos = new FileOutputStream(ApkFile); int count = 0; byte buf[] = new byte[1024]; do { int numread = is.read(buf); count += numread; progress = (int) (((float) count / length) * 100); /* 更新进度 */ handler.sendEmptyMessage(Msg.DOWN_UPDATE); Log.i("更新测试", "progress=" + progress); if (numread <= 0) { /* 下载完成通知安装 */ handler.sendEmptyMessage(Msg.DOWN_OVER); break; } fos.write(buf, 0, numread); } while (!interceptFlag);/* 点击取消就停止下载 */ fos.close(); is.close(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }; /** * 下载apk * * @param url */ private void downloadApk() { downLoadThread = new Thread(mdownApkRunnable); downLoadThread.start(); } /** * 安装apk * * @param url */ public void installApk() { File apkfile = new File(saveFileName); if (!apkfile.exists()) { return; } Intent apk = new Intent(Intent.ACTION_VIEW); apk.setDataAndType(Uri.parse("file://" + apkfile.toString()), "application/vnd.android.package-archive"); mContext.startActivity(apk); } public void uninstall(String pakageName) { Uri uri = Uri.parse(pakageName); Intent intent = new Intent(Intent.ACTION_DELETE, uri); mContext.startActivity(intent); } }
添加项目到Action Bar
你的fragment可以通过实现 onCreateOptionMenu() 提供菜单项给activity的选项菜单(以此类推, Action Bar也一样).为了使这个方法接收调用,无论如何, 你必须在 onCreate() 期间调用 setHasOptionsMenu() 来指出fragment愿意添加item到选项菜单(否则, fragment将接收不到对 onCreateOptionsMenu()的调用).
随后从fragment添加到Option菜单的任何项,都会被追加到现有菜单项的后面.当一个菜单项被选择, fragment也会接收到 对 onOptionsItemSelected() 的回调.也可以在你的fragment layout中通过调用 registerForContextMenu() 注册一个view来提供一个环境菜单.当用户打开环境菜单, fragment接收到一个对 onCreateContextMenu() 的调用.当用户选择一个项目, fragment接收到一个对onContextItemSelected() 的调用.
注意: 尽管你的fragment会接收到它所添加的每一个菜单项被选择后的回调, 但实际上当用户选择一个菜单项时, activity会首先接收到对应的回调.如果activity的on-item-selected回调函数实现并没有处理被选中的项目, 然后事件才会被传递到fragment的回调.
这个规则适用于选项菜单和环境菜单.
处理Fragment生命周期
管理fragment的生命周期, 大多数地方和管理activity生命周期很像.和activity一样, fragment可以处于3种状态:
Resumed
在运行中的activity中fragment可见.
Paused
另一个activity处于前台并拥有焦点, 但是这个fragment所在的activity仍然可见(前台activity局部透明或者没有覆盖整个屏幕).
Stopped
要么是宿主activity已经被停止, 要么是fragment从activity被移除但被添加到后台堆栈中.
停止状态的fragment仍然活着(所有状态和成员信息被系统保持着). 然而, 它对用户不再可见, 并且如果activity被干掉,他也会被干掉.
仍然和activity一样, 你可以使用Bundle保持fragment的状态, 万一activity的进程被干掉,并且当activity被重新创建的时候, 你需要恢复fragment的状态时就可以用到. 你可以在fragment的 onSaveInstanceState() 期间保存状态, 并可以在 onCreate(), onCreateView() 或 onActivityCreated() 期间恢复它.
生命周期方面activity和fragment之间最重要的区别是各自如何在它的后台堆栈中储存. 默认地, activity在停止后, 它会被放到一个由系统管理的用于保存activity的后台堆栈.(因此用户可以使用BACK按键导航回退到它).
然而, 仅当你在一个事务期间移除fragment时,显式调用addToBackStack()请求保存实例时,才被放到一个由宿主activity管理的后台堆栈.
另外, 管理fragment的生命周期和管理activity生命周期非常类似.因此, "managing the activity lifecycle"中的相同实践也同样适用于fragment. 你需要理解的是, activity的生命如何影响fragment的生命.
与activity生命周期的协调工作
fragment所生存的activity的生命周期,直接影响fragment的生命周期,每一个activity的生命周期的回调行为都会引起每一个fragment中类似的回调.
例如,当activity接收到onPause()时,activity中的每一个fragment都会接收到onPause().
Fragment 有一些额外的生命周期回调方法, 那些是处理与activity的唯一的交互,为了执行例如创建和销毁fragment的UI的动作. 这些额外的回调方法是:
-
onAttach()
当fragment被绑定到activity时被调用(Activity会被传入.). -
onCreateView()
创建和fragment关联的view hierarchy时调用. -
onActivityCreated()
当activity的onCreate()方法返回时被调用. -
onDestroyView()
当和fragment关联的view hierarchy正在被移除时调用. -
onDetach()
当fragment从activity解除关联时被调用.
fragment生命周期的流程, 以及宿主activity对它的影响, 在图3中显示.在这个图中,可以看到activity依次的每个状态是如何决定fragment可能接收到的回调方法.例如, 当activity接收到它的onCreate(), activity中的fragment接收到最多是onActivityCreated().
一旦activity到达了resumed状态, 你可以自由地在activity添加和移除fragment.因此,仅当activity处于resumed状态时, fragment的生命周期才可以独立变化.
无论如何, 当activity离开resumed状态,fragment再次被activity的推入它自己的生命周期过程.