接口和抽象类的区别:
- 抽象类可以有方法实现,但是接口不能
- 一个类可以实现(也可以说是继承)多个接口,但是只能继承自一个抽象类
- 接口注重的是功能的定义,抽象类强调的是所属关系
- 接口中的变量默认的是public static final的,并且必须赋初值,也就是不能有自己的私有成员;抽象类在这方面和普通的类差不多,可以有自己的私有成员
接口和抽象类的相同点:
- 都不能被实例化
- 抽象类的子类或者接口的实现类必须实现抽象类或者接口的全部抽象方法
Home键:默认情况下,android中的home键只是切换到系统的home界面,并不会销毁当前的activity,即不会执行activity中的ondestroy方法,从任务栈中恢复那个应用的话,activity中的数据不会丢失
Back键:而android中的back键,则先销毁当前activity,然后回到之前的应用界面中,而且此时activity中的数据都丢失了。
如果我们想在按back键的时候,不销毁当前的activity,保留它的数据和状态的话,我们可以改写源码中对back键的处理来达到这个目的,代码如下:
1 2 3 4 5 6 7 8 9 10 11 |
@Override public void onBackPressed() { //屏蔽下面的这行代码, 应用就不会执行默认的back键操作了 //super.onBackPressed(); //下面的代码是模仿回到home界面的代码 Intent i= new Intent(Intent.ACTION_MAIN); i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); i.addCategory(Intent.CATEGORY_HOME); startActivity(i); } |
当然这样做的缺点时,之前打开的应用全部都被关闭掉了,比如用户在用浏览器看新闻,通过通知栏消息打开了你的应用,在你的应用中按back键就回到了home界面,前面的浏览器也被关掉了。
# yum install httpd
# yum install httpd-devel
# yum install subversion
# yum install mod_dav_svn
# yum install mod_auth_mysql
验证是否安装成功,输入命令:
# svnserve –version
1 2 3 4 5 6 7 8 9 10 11 12 13 |
svnserve, version 1.6.11 (r934486) compiled Feb 10 2015, 22:08:22 Copyright (C) 2000-2009 CollabNet. Subversion is open source software, see http://subversion.tigris.org/ This product includes software developed by CollabNet (http://www.Collab.Net/). The following repository back-end (FS) modules are available: * fs_base : Module for working with a Berkeley DB repository. * fs_fs : Module for working with a plain file (FSFS) repository. Cyrus SASL authentication is available. |
输出上面的信息后证明安装成功
首先创建一个目录用于存放svn的各个项目
# mkdir /opt/svn
使用svnadmin创建一个代码库
# svnadmin create /opt/svn/repo1
这样第一个项目的代码库就创建完成了,进入到repo1目录中,可以看到conf, db,format,hooks, locks, README.txt等文件,说明代码库建立成功
进入到/opt/svn/repo1/conf目录中,可以看到三个文件authz, passwd, svnserve.conf,其中authz是用于配置用户组和用户的文件,passwd是用于设置用户名和密码的文件,svnserve.conf是用于配置服务器的文件
输入下面命令编辑passwd文件
# vi /opt/svn/repo1/conf/passwd
在[users]节点下面以下面格式输入“用户名 = 密码”,如:bcoder = 123456
输入下面命令编辑authz文件
# vi /opt/svn/repo1/conf/authz
[groups]节点下面是用于配置用户组的,可在每行输入”组名 = 用户1,用户2,用户3……”,如:” grpdeveloper = dev1, dev2, dev3 ”
[代码库路径]用于配置用户或者组对该代码库的权限,可在每行输入“用户名=rw”,r是读权限,rw是读写权限。如果对用户组则需要在用户组名前面加上 “@”,如:“@grpdeveloper”
这里我们简单的设置如下:
1 2 |
[/] bcoder = rw |
输入下面的命令编辑svnserve.conf
# vi /opt/svn/repo1/conf/svnserve.conf
1 2 3 4 5 6 |
[general] # anon-access = read # auth-access = write # password-db = passwd # authz-db = authz # realm = My First Repository |
anon-access用于设置匿名用户的权限,read为只读,write为读写,none为不能访问,通常设置为none
auth-access用于设置登录用户的权限,read为只读,write为读写,none为不能访问,通常设置为none
password-db用于指定存储用户名密码的文件,即/opt/svn/repo1/conf/中的passwd文件,默认password-db = passwd即可
authz-db用于指定用户权限的文件,即/opt/svn/repo1/conf/中的authz文件,默认authz-db=authz即可
输入下面的命令启动svn服务器
# svnserve -d -r -/opt/svn/repo1
在客户端使用下面地址checkout,svn://你的IP地址/
输入下面的命令停止svn服务器
# killall svnserve
假设我们在/opt/svn/下面建立了多个代码库,如
# svnadmin create /opt/svn/repo1
# svnadmin create /opt/svn/repo2
# svnadmin create /opt/svn/repo3
那么如果我们想使用多个代码库,在启动svn服务器的时候使用如下命令
# svnserve -d -r /opt/svn/
即使用多个代码库的上一级目录启动svn服务器,现在在客户端checkout的时候就要输入代码库的名字了,如:svn://你的IP地址/repo1
uses RichEdit;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
procedure TForm1.Button3Click(Sender: TObject); var pf: PARAFORMAT2; begin FillChar(pf, sizeof(paraformat2), #0); pf.cbSize := SizeOf(paraformat2); pf.dwMask := PFM_LINESPACING ; //需要设置上 PFM_LINESPACING 标志,bLineSpacingRule和dyLineSpacing才可能有效 // pf.bLineSpacingRule := 0; //单倍行距,dyLineSpacing的值将被忽略 // pf.bLineSpacingRule := 1; //1.5倍行距,dyLineSpacing的值将被忽略 // pf.bLineSpacingRule := 2; //两倍行距,dyLineSpacing的值将被忽略 // pf.bLineSpacingRule := 3; //用dyLineSpacing以缇为单位指定行间距,当此值小于单倍行距时,效果为单倍行距 // pf.bLineSpacingRule := 5; //用dyLineSpacing/20指定行间距 pf.bLineSpacingRule := 4; //用dyLineSpacing以缇为单位指定行间距 pf.dyLineSpacing := RichEdit1.Font.Size * 20 + 20 * 4; //这是笔者大概计算的,可以根据字体大小调节的,行间最小距离,大字体时可能出现上下行重叠,可以设置为300或者自己计算 RichEdit1.SelectAll; //只对选择的文本有效,***重要*** SendMessage(RichEdit1.Handle, EM_SETPARAFORMAT, 0, LPARAM(@pf)); RichEdit1.SelStart := 0; RichEdit1.SelLength := 0; end; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
procedure TForm1.Button4Click(Sender: TObject); const PFNS_PAREN = $000; //e.g. 1) PFNS_PARENS = $100; //e.g. (1) PFNS_PERIOD = $200; //e.g. 1. PFNS_PLAIN = $300; PFNS_NONUMBER = $400; const PFN_NONE = $00000000; //无 PFN_BULLET = $00000001; //黑色实心圆点 PFN_ARABIC = $00000002; //0,1,2 PFN_LCLETTER = $00000003; //a,b,c PFN_UCLETTER = $00000004; //A,B,C PFN_LCROMAN = $00000005; //i,ii,iii PFN_UCROMAN = $00000006; //I,II,III var pf: PARAFORMAT2; begin FillChar(pf, sizeof(paraformat2), #0); pf.cbSize := SizeOf(paraformat2); //PFM_NUMBERING: wNumbering 值有效 //PFM_NUMBERINGSTYLE: wNumberingStyle值有效 //PFM_NUMBERINGSTART: wNumberingStart值有效 //PFM_STARTINDENT: dxStartIndent值有效 pf.dwMask := PFM_NUMBERING or PFM_NUMBERINGSTYLE or PFM_NUMBERINGSTART or PFM_STARTINDENT;//or PFM_OFFSET; pf.wNumberingStyle := PFNS_PERIOD; //设置行号的样式,可以为“)”,“.”,“()” pf.wNumberingStart := 1; //设置行号起始值 pf.wNumbering := PFN_ARABIC; //设置行号的格式,可以为阿拉伯数字或者英文字母等格式 pf.dxStartIndent := 60; //设置行首缩进值 RichEdit1.SelectAll; SendMessage(RichEdit1.Handle, EM_SETPARAFORMAT, 0, LPARAM(@pf)); RichEdit1.SelStart := 0; RichEdit1.SelLength := 0; end; |
当RichEdit为可用状态时,是不用代码控制此操作的,TRichEdit本身就可以定位光标。但是当RichEdit开始不可用时,则此方法就可能用到了。本例中RichEdit1开始时是不可用的,当在ApplicationEvents1中接收到RichEdit1被双击时则设置RichEdit1的Enable := true;并且定位光标。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls, RichEdit, AppEvnts; type TForm1 = class(TForm) RichEdit1: TRichEdit; ApplicationEvents1: TApplicationEvents; procedure EnableRichEdit; procedure ApplicationEvents1Message(var Msg: tagMSG; var Handled: Boolean); private { Private declarations } public { Public declarations } end; var Form1: TForm1; FLastMousePos: TPoint; implementation {$R *.dfm} procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG; var Handled: Boolean); var mPos: TPoint; begin if msg.message = WM_LBUTTONDBLCLK then begin if msg.hwnd = RichEdit1.Parent.Handle then begin GetCursorPos(FLastMousePos); EnableRichEdit; end; end ; end; procedure TForm1.EnableRichEdit; var cPos: TPoint; retCode: integer; mR, mC: Word; begin RichEdit1.Enabled := True; RichEdit1.SetFocus ; RichEdit1.SelStart := 0; if (FLastMousePos.X = 0) and (FLastMousePos.Y = 0) then GetCursorPos(FLastMousePos); FLastMousePos := RichEdit1.ScreenToClient(FLastMousePos); retCode := SendMessage(RichEdit1.Handle, EM_CHARFROMPOS, 0, LPARAM(@FLastMousePos)); mR := HiWord(retCode); mC := Loword(retCode); cPos.X := mC ; cPos.Y := mR ; RichEdit1.CaretPos := cPos; FLastMousePos.X := 0; FLastMousePos.Y := 0; end; end. |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls, RichEdit, ShellAPI; type TForm1 = class(TForm) RichEdit1: TRichEdit; procedure FormCreate(Sender: TObject); private { Private declarations } public procedure WndProc(var Msg: TMessage); override; end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject); var mask: Word; begin mask := SendMessage(RichEdit1.Handle, EM_GETEVENTMASK, 0, 0); SendMessage(RichEdit1.Handle, EM_SETEVENTMASK, 0, mask or ENM_LINK); SendMessage(RichEdit1.Handle, EM_AUTOURLDETECT, Integer(True), 0); end; procedure TForm1.WndProc(var Msg: TMessage); var p: TENLink; sURL: string; CE : TRichEdit; begin if (Msg.Msg = WM_NOTIFY) then begin if (PNMHDR(Msg.lParam).code = EN_LINK) then begin p := TENLink(Pointer(TWMNotify(Msg).NMHdr)^); if (p.Msg = WM_LBUTTONDOWN) then begin try CE := tRichEdit(Self.ActiveControl); SendMessage(CE.Handle, EM_EXSETSEL, 0, Longint(@(p.chrg))); sURL := CE.SelText; ShellExecute(0, 'open', PChar(sURL), 0, 0, SW_SHOWNORMAL); except end; end; end; end; inherited; end; end. |
在Delphi开发中,如果我们想让一个窗口始终置顶显示,则我们只需要把窗口FormStyle属性设置为fsStayOnTop就可以了,但是如果这个窗口不是主窗口,而是子窗口,那就有些麻烦了,设置FormStyle为fsStayOnTop后也无效。解决办法就是在子窗口中重载CreateParams函数,并将WndParent设置为0即可,具体代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
unit Unit2; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls; type TForm2 = class(TForm) Button1: TButton; Timer1: TTimer; private { Private declarations } public { Public declarations } protected procedure CreateParams(var Params: TCreateParams); override; end; var Form2: TForm2; implementation {$R *.dfm} { TForm2 } procedure TForm2.CreateParams(var Params: TCreateParams); begin inherited; Params.WndParent := 0; // Params.ExStyle := Params.ExStyle or WS_EX_TOOLWINDOW; //如果不想在任务栏显示窗口图标 end; end. |
SharedPreferences貌似很简单的一个类,却也存在着陷阱,如下面,我们写了几行简单的保存配置的代码,但却隐含着bug
1 2 3 |
SharedPreferences prefs = getSharedPreferences("config", Context.MODE_PRIVATE); prefs.edit().putInt("runcount", 100); prefs.edit().apply(); |
那么为什么这段代码不能保存runcount的数据呢,因为prefs.edit()每次返回一个新的Editor对象,所以执行两次prefs.edit()其实调用的是两个Editor对象,如下adnroid源码中的解释
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/** * Create a new Editor for these preferences, through which you can make * modifications to the data in the preferences and atomically commit those * changes back to the SharedPreferences object. * * <p>Note that you <em>must</em> call {@link Editor#commit} to have any * changes you perform in the Editor actually show up in the * SharedPreferences. * * @return Returns a new instance of the {@link Editor} interface, allowing * you to modify the values in this SharedPreferences object. */ Editor edit(); |
因此,我们应该先将prefs.edit()赋值给一个Editor对象,然后由该对象进行存值和提交操作,如下:
1 2 3 4 |
SharedPreferences prefs = getSharedPreferences("config", Context.MODE_PRIVATE); Editor editor = prefs.edit(); editor.putInt("runcount", 100); editor.apply(); |
出现此问题的原因是你的项目使用到的android的appcompat支持库,但你目前的开发环境中并没有引用此支持库。下面图文介绍如何下载并引用该类库。
1、首先需要在android sdk manager中下载Android Support Library,在Eclipse中打开菜单Window->Android SDK Manager,在sdk manager窗口中看你是否已经下载了Android Support Library,在最下面的Extras的节点中有此项,如果没有则选中此项并下载,如下图
类库将会下载到你的sdk子目录中,路径为<sdk目录>extrasandroidsupport。如果你使用的Android Studio开发环境,则需要下载Android Support Respository。
2、将类库项目导入到开发环境中。在Eclipse菜单中打开File->New->Project,选择Android Project From Existing Code
3、点击”Browser…”按钮选择appcompat所在的路径,<sdk目录>extrasandroidsupportv7appcompat,勾选Copy projects into workspace,最后点击“Finish”按钮,导入项目成功。
4、将appcompat库引用到你的项目中。在你的项目上右键选择属性菜单,在左侧的导航列表中选择“Android”,在右侧内容最下的Library区域点击“Add”按钮,将导入的类库项目添加进来就完成了。
5、重新Clean你的项目。点击Eclipse的菜单Project->Clean…,在弹出的窗口中选择你要清除的项目,点击ok,清除结束后上述问题应该就可以解决了。
关于Android支持库的其他特性可以点击这里查询。
在有些APP中我们需要实现一个悬浮按钮,比如图片浏览应用左右翻页功能,比如左侧悬浮功能按钮。我们要实现此功能时,最开始想到的就是用FrameLayout来实现,但是如果把按钮简单的放到FrameLayout中,按钮只能在布局的左上角,并不能达到我们的要求。
那么如何才能让按钮放到布局的左侧中间位置呢,开始时想把按钮高度设置成match_parent,然后图片垂直居中,但是这样整个最左侧的部分都是按钮了,显然会影响其他功能的使用,后来想哪个组件才能自由的让子控件放到想放的位置呢,这个得是RelativeLayout了吧。于是在FrameLayout中放了一个RelavtiveLayout中,然后把按钮放到里面,Layout Parameters中的Align Parent Left设置成true,Center Vertical设置成true,再看一下,一个居中的悬浮按钮就弄好了。
同样的方式可以设置其他需要的悬浮按钮了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<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" tools:context="com.example.hoverbuttontest.MainActivity" > <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" > <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" > <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_centerVertical="true" android:text="Button" /> <Button android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignBottom="@+id/button1" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:text="Button" /> </RelativeLayout> </FrameLayout> </LinearLayout> |
做了一个小的app,其中用到了tab形式的actionBar和多个fragment,后来因为需求需要在某两个fragment中加入context menu,加入后添加onContextItemSelected事件响应菜单按下的效果,一开始运行正常,在两个fragment中打开context menu正常,两个fragment中的菜单响应事件也正常,后来改动了一些代码后突然发现菜单响应的不正确了,只有一个fragment中的菜单有响应,另外一个没响应。经过一番修改后,发现是onContextItemSelected的返回值影响到了菜单点击事件的执行。
我们先来看一下onContextItemSelected的官方描述:
1 2 3 4 5 6 7 8 9 10 11 |
This hook is called whenever an item in a context menu is selected. The default implementation simply returns false to have the normal processing happen (calling the item's Runnable or sending a message to its Handler as appropriate). You can use this method for any items for which you would like to do processing without those other facilities. Use getMenuInfo() to get extra information set by the View that added this menu item. Derived classes should call through to the base class for it to perform the default menu handling. Parameters item The context menu item that was selected. Returns boolean Return false to allow normal context menu processing to proceed, true to consume it here. |
我们主要看这个返回值部分,返回为了一布尔型变量,当返回false值时由系统去决定此菜单事件的响应流程,返回true则表示菜单响应到此为止,停止后面的流程。
本人没做深入研究,猜想大概流程是这样的,因为在fragment中调用context menu中通过如下方式
1 2 3 4 |
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { getActivity().getMenuInflater().inflate(R.menu.menu_goodlist_item, menu); } |
是通过父activity来填充这个弹出菜单,所以首先是父activity先接收到onContextItemSelected事件,如果父activity中不做处理,则activity会把这个事件分发到各个fragment中去,本人在此次犯的错误就是在子fragment中的onContextItemSelected事件中返回了true值,所以只在一个fragment中响应了事件,另外一个没有响应。
既然在所有的fragment中都会响应onContextItemSelected这个事件,那么我们在每个fragment中处理该事件的时候就需要做一些判断,以避免多个fragment同时执行这个事件,这个是非常重要的,因为不做判断很可能出现一些莫名奇妙的而且不好测试的问题。如下例子:
1 2 3 4 5 6 7 8 9 |
@Override public boolean onContextItemSelected(MenuItem item) { super.onContextItemSelected(item); if(item.getItemId() == R.id.menu_edit_good || item.getItemId() == R.id.menu_delete_good){ ... } return false; } |