使用了 ARC 之后,无疑大大减少了程序员进行内存管理的压力。你再也不用写 release/autorelease 代码了,再也不用写dealloc 方法了。但这不等于程序员不需要内存管理。例如,你需要在 viewDidUnload 方法中对对象进行释放,以防当内存警告发生时,你没有内存可以回收。当然ARC 托管对象的释放方式与 MRC 是不同的。对于 ARC 托管对象,你必须用 ARC 特有的两种释放方式:
[self setOjbect:nil];
或者:
self.object=nil;
这里,object 是你要释放的对象名称。
这两种释放方式基本上是等效的,甚至许多书籍宣称二者完全等效。对于有 Java/C++/C# 开发经验的程序员来说,点语法的调用显然更加亲切。
但我要告诉你的是,二者并不是完全等同。对于 object 对象并未初始化(为nil)的情况下,[self setObject:nil] 显得更安全。
如果 object 未初始化,self.object=nil 调用会导致一个“向已销毁的对象发送消息”错误,同时程序崩溃。 而[selfsetObject:nil]则根本不会。
注意:iPad 的内存管理似乎要宽泛许多,一些在iPad 上能够正常运行的代码,在 iPhone 上却会导致内存管理失败。
这是由于 ARC 会在合成属性 object 的访问方法时,添加"自动平衡"代码,它会自动检测对象的alloc/release 调用并以一种极聪明的方式进行平衡。
因此,对于那些习惯于 Java/C++ 语言中“.”语法泛滥的程序员来说,应该尽量抛弃对 O-C 的偏见,回到消息调用语法的“正途”上来。
特别对于对象的释放,一定要小心从事,尽量使用[self setObject:nil]的方式,而不要使用“.”语法。因为,你可以向一个 nil 对象发送“消息”,却不能向一个 nil对象进行赋值。
MainActivity如下:
package cc.testpropertyanimation; import android.os.Bundle; import android.view.View; import android.widget.RelativeLayout; import android.animation.AnimatorInflater; import android.animation.ArgbEvaluator; import android.animation.ObjectAnimator; import android.app.Activity; import android.content.Context; /** * Demo描述: * 利用Property Animation(属性动画)使组件的背景色渐变 * * 参考资料 * Android疯狂讲义(第二版) 作者李刚 * Thank you very much * */ public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); init(); } private void init(){ RelativeLayout relativeLayout=(RelativeLayout) findViewById(R.id.relativeLayout); AnimationView animationView=new AnimationView(this); relativeLayout.addView(animationView); } public class AnimationView extends View{ public AnimationView(Context context) { super(context); ObjectAnimator objectAnimator= (ObjectAnimator) AnimatorInflater.loadAnimator(MainActivity.this, R.animator.coloranimation); objectAnimator.setEvaluator(new ArgbEvaluator()); objectAnimator.setTarget(this); objectAnimator.start(); } } }
coloranimation.xml如下:
<?xml version="1.0" encoding="utf-8"?> <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:propertyName="backgroundColor" android:duration="5000" android:valueFrom="#ff0033" android:valueTo="#000099" android:repeatCount="infinite" android:repeatMode="reverse" android:valueType="intType"> </objectAnimator>
main.xml如下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/relativeLayout" android:layout_width="match_parent" android:layout_height="match_parent" > </RelativeLayout>
工作需要实现一个在播放音乐时,音量键按下后弹出程序自己的音量控制界面,由于对android还不太熟悉,就当写个小Demo练练手吧。
根据要求的话,需要实现如下功能点的要求
1、 拦截系统音量键
2、 用SeekBar简单实现音量控制界面(采用对话框的形式来实现)
3、 点击对话框外内容,对话框自动消失
4、 对话框内一定时间内无事件(3s),对话框自动消失
下面分步来实现这个小Demo程序。
拦截系统音量键本来想在View中拦截音量键的,但是不知道为什么在View中onKeyDown事件拦截不到,后来查了下网上的说法,如果需要让一个View响应按键事件,必须按键的时候焦点位于这个View上面,我尝试了下,假设这个View类实例为test,则可以通过下列方式设置焦点
//test.setFocusable(true);
test.setFocusableInTouchMode(true);
test.requestFocus();
网上也有人说通过SetFocusable设置的就可以了,但是我试了下不行,必须得用setFocusableInTouchMode然后调用requestFocus,我在测试时放在OnCreate方法中调用,也有人说放在OnCreate中有时候也不行,因为此时View还没有被显示出来。后来问了下同事,说是这种在View中拦截OnKeyDown的方式是并不推荐的方法,有违面向对象的原则,OnKeyDown这种事件本身就应该交由Activity等类来拦截,然后需要处理的操作,就通过给View类配备set和get方法的形式来设置,想想也是很有道理的。
系统音量键的拦截网上代码很多,在这里也不多说,直接上代码吧,其中的VolumeSetDialog就是自己的音量控制对话框类。
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
VolumeSetDialog vDlg = new VolumeSetDialog(this);
vDlg.show();
return true;
} else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
VolumeSetDialog vDlg = new VolumeSetDialog(this);
vDlg.show();
return true;
} else {
return super.onKeyDown(keyCode, event);
}
}
这里真没有什么补充的,因为我的对话框弹出时,跟系统的音量控制对话框是互斥的,所以onKeyDown返回为true,否则还是会显示出系统音量对话框出来哦。
音量控制对话系统提供的SeekBar是横向的,我本来以为可以通过类似android:orientation="vertical"的修改参数方法让其实现竖直方向,结果不行,貌似需要用自定义的seekBar,想着能力有限,就给几个链接吧,以后有需要再去看
android 竖直seekbar的案例,需要重载类
http://www.eoeandroid.com/thread-93431-1-1.html
http://blog.csdn.net/riyuegonghe/article/details/9298567
http://www.mokasocial.com/2011/02/create-a-custom-styled-ui-slider-seekbar-in-android/
我就使用默认的最简单的拖动方法产生了一个布局名为volume_dialog.xml,具体的内容如下
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/volume_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<SeekBar
android:id="@+id/volume_seekBar"
android:layout_width="200dp"
android:layout_height="30dp"
android:layout_centerInParent="true" />
</RelativeLayout>
注:上面的代码中
然后构建一个新的派生自AlertDialog的VolumeSetDialog类。在该类中,重载AlertDialog的onCreate函数,具体代码如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
LayoutInflater inflater = LayoutInflater.from(getContext());
View mRootView = getLayoutInflater().inflate(R.layout.dj_volume_dialog, null);
setContentView(mRootView);
// LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT,
// LayoutParams.MATCH_PARENT);
// addContentView(mRootView, params);
mVolumeSeekBar = (SeekBar) mRootView.findViewById(R.id.volume_seekBar);
mVolumeSeekBar.setOnSeekBarChangeListener(this);
//mVolumeSeekBar.setOnClickListener(this);
//mRootView.findViewById(R.id.volume_layout).setOnClickListener(this);
mAudioManager = (AudioManager) this.getContext().getSystemService(Context.AUDIO_SERVICE);
mMaxMusicVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
mCurMusicVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
mVolumeSeekBar.setMax(mMaxMusicVolume);
mVolumeSeekBar.setProgress(mCurMusicVolume);
setCancelable(true);
setCanceledOnTouchOutside(true);
}
上述代码加红色字体是我这个初学者一开始犯错误的地方。刚开始没有调用addContentView和setContentView,发现调用dlg.show的时候,界面变灰了,但是始终没有出现SeekBar,后来问了同事才知道,需要吧inflater的View加载到对话框中才能显示出来。
后面两块红色字体其实属于第三项功能了。
在对话框外点击,对话框消失刚开始考虑问题时,想到的是在SeekBar和整个根视图上添加OnClickListener,然后重载onClick(View v)方法,判断当前点击的是否为SeekBar,假如不是SeekBar就调用dismiss函数让整个对话框消失。
后来发现,其实onClick事件压根就没有被调到,后来给布局加上了一个button,结果点击的时候onClick事件才能响应,以为要设置SeekBar的clickable属性,结果设置了也是没有用。决定放弃,硬着头皮又去问同事,才知道对话框提供了setCanceledOnTouchOutside和setCancelable方法,前者设为true时,就能实现对话框外点击,对话框消失,设为false时,点击无响应;而后者则是对系统back键的响应与否,true则点击back键对话框消失。
在这里需要补充的时,原本以为布局文件要进行更改,不能使用match_parent,以为match_parent就覆盖了整个Windows,结果并非如我想的那样,应该是对话框属性默认就是将控件居中显示的,所以布局文件中就算将android:layout_centerInParent="true"属性去除,在弹出对话框显示时还是会居中显示SeekBar的。
至于具体的对话框样式,这里就不展开追究了。
对话框内一定时间内无事件(3s),对话框自动消失这个功能的实现,可以使用Timer和TimerTask来实现,不过起初想的稍微有点复杂,把TimerTask也实例化一个对象来使用以便管理取消的任务,谁知道TimerTask实例在Timer取消的时候,压根也被释放掉了,不能重用,Timer实例对象一旦cancel也是不能被再次schedule了,否则会报错,具体可以参见如下两个链接的说明
http://blog.csdn.net/snowdream86/article/details/7072530
http://www.eoeandroid.com/thread-68735-1-1.html
废话不多说,感觉android真的不用你像C++一样需要考虑得太多,他很多都已经给你弄好了,上传一个简单的控制代码吧,呵呵。
private void startTimer() {
if (mTimer != null) {
mTimer.cancel();
mTimer = null;
}
mTimer = new Timer();
mTimer.schedule(new TimerTask() {
@Override
public void run() {
VolumeSetDialog.this.dismiss();
mTimer.cancel();
mTimer = null;
}
}, 3000);
}
private void stopTimer() {
if (mTimer != null) {
mTimer.cancel();
mTimer = null;
}
}
好了,一个简单的小例子就介绍到这里,做完了感觉很简单,但是说实话,对于我这种android新手还是花了不少时间才搞懂的。