今天,我的第一个APP:番茄工作法 1.0版本终于终于终于完成了,虽然还有很多很多的不足之处,但是,终归算是告一个段落了。
第一款小应用,其中的艰辛冷暖自知,各种摸爬滚打,各种度娘谷歌。
简单讲解下其中碰到的问题:
问题一:android.support.v4.app.Fragment 包下没有PreferenceFragment的问题。
起初做的设置界面太过难看,所以打算使用谷歌力推的碎片机制,但是发现v4包下面居然没有PreferenceFragment类。然后各种搞不定。
最后在谷歌的帮助下顺利找到实现方法(花了我将近一个礼拜的时间,泪奔,新手不解释...)
进入上面的链接下载这个library。
实现方式和android.preference.PreferenceFragment 下的方式一样,具体代码如下:
public class fragment3 extends PreferenceFragment{ public static fragment3 newInstance(Bundle bundle) { fragment3 frag = new fragment3(); frag.setArguments(bundle); return frag; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.preferences); Log.v("huahua", "fragment3-->onCreate()"); } }
具体代码我会在下面全部放出来
其中有一点不足,相当遗憾:preferences.xml中的RingtonePreference设置铃声的数据一直无法保存到SharedPreferences中去,导致无法设定铃声。如果有大神能够解决
感激不敬。
注意在preferences.xml中最好不要设定自定义的View,因为这个问题困扰了我很久。
关于PreferenceFragment的其他用法:http://www.oschina.net/question/565065_107985
由于PreferenceFragment中没有SeekBar 重写SeekBar的方式介绍:http://www.eoeandroid.com/thread-115052-1-1.html
问题二:保存简单数据的问题
在应用中可能需要保存一些很简单的数据,如界面上显示一个执行任务的次数等,这些简单数据可以放到SharedPreferences,它会将数据保存到一个xml中去,具体的使用代码如下:
SharedPreferences mySharedPreferences = getActivity().getSharedPreferences("TomatoCount", Activity.MODE_PRIVATE);//获取SharedPreferences 中的值,TomatoCount表示保存的文件名称 String dateStr = mySharedPreferences.getString("date", "2001-01-01");//获取字符串 todayTomatoCount = mySharedPreferences.getInt("todayTomatoCount", 0);//获取存储的今日番茄时间,获取int型数据,如果不存在默认设置为0 allTomatoCount = mySharedPreferences.getInt("allTomatoCount", 0);//获取存储的合计番茄时间 String dateNowString = (new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())).format(new java.util.Date()); if (!dateStr.equals(dateNowString)) {//判断存储时间是否和当前时间在同一天 todayTomatoCount=0; SharedPreferences.Editor editor = mySharedPreferences.edit(); editor.putInt("todayTomatoCount", todayTomatoCount);//写入数据到Editor 其中第一个参数是字段的名称,第二个参数是字段的值,该写入的是int类型 editor.commit();//提交,写入到xml文件中 }
问题三:修改字体
在assets文件夹下新建fonts文件夹,将需要的字体(*.ttf)放入到该文件夹下,代码中使用该字体的方法如下:
//修改字体 Typeface fontFace = Typeface.createFromAsset(getActivity().getAssets(), "fonts/Roboto-Thin.ttf"); tomatoTxtView.setTypeface(fontFace);
问题四:如何在PreferenceFragment实现类中如果要实时的获取修改的值等
如果当ListPreference中的值改变以后,我要在ListPreference的副标题中显示改变的值该如何操作呢:重写onSharedPreferenceChanged方法
如果当我们需要点击preference进入到另外一个页面时,或者我们需要跳转到网页时,该如何操作呢:重写onPreferenceTreeClick方法
public class SettingPreferenceFragment extends PreferenceFragment implements OnSharedPreferenceChangeListener { ListPreference lstPre_TomatoTime_value, lstPre_BreakTime_value; public SettingPreferenceFragment() { // TODO 自动生成的构造函数存根 } @Override public void onCreate(Bundle paramBundle) { // TODO 自动生成的方法存根 super.onCreate(paramBundle); addPreferencesFromResource(R.xml.preferences); SharedPreferences prefs=PreferenceManager.getDefaultSharedPreferences(getActivity()); prefs.registerOnSharedPreferenceChangeListener(this); lstPre_TomatoTime_value=(ListPreference)findPreference("TomatoTime_value"); lstPre_BreakTime_value=(ListPreference)findPreference("BreakTime_value"); lstPre_TomatoTime_value.setSummary(lstPre_TomatoTime_value.getEntry()); lstPre_BreakTime_value.setSummary(lstPre_BreakTime_value.getEntry()); } @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { // TODO 自动生成的方法存根 if (key.equals("TomatoTime_value")) { lstPre_TomatoTime_value.setSummary(lstPre_TomatoTime_value.getEntry()); } if (key.equals("BreakTime_value")) { lstPre_BreakTime_value.setSummary(lstPre_BreakTime_value.getEntry()); } } @Override public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { // TODO 自动生成的方法存根 if (preference.getKey().equals("clearCount")) { alertDialogShow(); } if (preference.getKey().equals("aboutTomatoTask")) { Uri uri = Uri.parse("http://baike.baidu.com/link?url=b7rlhS6YssFup2xqAjnw9__6VsQnyhtVT8Gx_-qwckUE4IZ-ns6i_jw9w_aKH-C_sjWheb9NFR_GZcfUII0bV_"); startActivity(new Intent(Intent.ACTION_VIEW,uri)); } return false; } /** * 显示AlertDialog */ private void alertDialogShow() { new AlertDialog.Builder(getActivity()).setTitle("清除?").setMessage("是否清除计数?\n注:该操作不可逆!").setPositiveButton("清除", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // TODO 自动生成的方法存根 SharedPreferences mySharedPreferences = getActivity().getSharedPreferences("TomatoCount", Activity.MODE_PRIVATE); SharedPreferences.Editor editor = mySharedPreferences.edit(); editor.putInt("todayTomatoCount", 0); editor.putInt("allTomatoCount", 0); editor.commit(); Toast.makeText(getActivity(), "清除成功!", Toast.LENGTH_SHORT).show(); } }).setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // TODO 自动生成的方法存根 } }).create().show(); } }
在android 已经给我们封装好了一个倒计时的类,我们直接拿来实现就可以啦,具体过程如下:
private TimeCount time;2. new 一个TimeCount, timeSpan是需要倒计时的时间(毫秒),1000是倒计时间隔,这里是一秒
time = new TimeCount(timeSpan, 1000);// 构造CountDownTimer对象 time.start();
3. 写内部类TimeCount 继承自CountDownTimer 其中onTick表示,在上述设定的倒计时间隔期间做什么,onFinish表示计时完毕时做什么
class TimeCount extends CountDownTimer { public TimeCount(long millisInFuture, long countDownInterval) { super(millisInFuture, countDownInterval);// 参数依次为总时长,和计时的时间间隔 } /** * 计时过程显示 */ @Override public void onTick(long millisUntilFinished) { // TODO 自动生成的方法存根 } /** * 计时完毕时触发 */ @Override public void onFinish() { // TODO 自动生成的方法存根 } }
问题六:触发点击事件:当我们点击返回按键时,是否弹出AlertDialog提示框,或者是提示“再按一次退出”的实现:
@Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (flag == 2) { if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) { AlertDialog.Builder alertBuilder = new AlertDialog.Builder( MainActivity.this); alertBuilder .setTitle("放弃?") .setMessage("是否放弃这个番茄并退出吗?") .setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // TODO 自动生成的方法存根 time.cancel(); MainActivity.this.finish(); } }) .setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // TODO 自动生成的方法存根 dialog.cancel(); } }).create(); alertBuilder.show(); } } else { if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) { if ((System.currentTimeMillis() - exitTime) > 2000) { Toast.makeText(getApplicationContext(), "再按一次退到主界面", Toast.LENGTH_SHORT).show(); exitTime = System.currentTimeMillis(); } else { time.cancel(); finish(); System.exit(0); } return true; } return super.onKeyDown(keyCode, event); } return true; }
问题七:震动的实现
private Vibrator vibrator;
//开启震动 vibrator =(Vibrator)getSystemService(Context.VIBRATOR_SERVICE); long [] pattern = {200,500,200,500,1200,500,200,500}; // 停止 开启 停止 开启 vibrator.vibrate(pattern,-1); //重复两次上面的pattern 如果只想震动一次,index设为-1
package com.android.tomatotask; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.RectF; import android.util.AttributeSet; import android.view.View; public class CircleProgressBar extends View { private int maxProgress = 10;//最大进度 private int progress = 0;//当前进度 private int progressStrokeWidth = 6;//线宽 // 画圆所在的矩形区域 RectF oval; Paint paint; public CircleProgressBar(Context context) { super(context); // TODO 自动生成的构造函数存根 } public CircleProgressBar(Context context, AttributeSet attrs) { super(context, attrs); // TODO 自动生成的构造函数存根 oval = new RectF(); paint = new Paint(); } public CircleProgressBar(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // TODO 自动生成的构造函数存根 } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); /** * 画最外层的大圆环 */ int centre = getWidth()/2; //获取圆心的x坐标 int radius = (int) (centre - progressStrokeWidth/2); //圆环的半径 paint.setColor(Color.WHITE);//(roundColor); //设置圆环的颜色 paint.setStyle(Paint.Style.STROKE); //设置空心 paint.setStrokeWidth(progressStrokeWidth); //设置圆环的宽度 paint.setAntiAlias(true); //消除锯齿 canvas.drawCircle(centre, centre, radius, paint); //画出圆环 /** * 画圆弧 ,画圆环的进度 */ //设置进度是实心还是空心 paint.setStrokeWidth(progressStrokeWidth); //设置圆环的宽度 paint.setColor(Color.rgb(0x57, 0x87, 0xb6)); //设置进度的颜色 RectF oval = new RectF(centre - radius, centre - radius, centre + radius, centre + radius); //用于定义的圆弧的形状和大小的界限 paint.setStyle(Paint.Style.STROKE); canvas.drawArc(oval, -90, 360 * progress / maxProgress, false, paint); //根据进度画圆弧 绘制白色圆圈,即进度条背景 } public int getMaxProgress(){ return maxProgress; } public void setMaxProgress(int maxProgress){ this.maxProgress = maxProgress; } public void setProgress(int progress){ this.progress = progress; this.invalidate(); } public void setProgressNotInUiThread(int progress){ this.progress = progress; this.postInvalidate(); } }
相关XML使用书写方式:
<com.android.tomatotask.CircleProgressBar android:id="@+id/circleProgressbar" android:layout_width="300dp" android:layout_height="300dp" android:layout_centerInParent="true" /> <android.support.v4.view.ViewPager android:id="@+id/viewpage" android:layout_width="match_parent" android:layout_height="match_parent" />
protected Animation animation;
// 动画资源文件 ID = new int[] { R.anim.my_alpha_action, R.anim.my_scale_action, R.anim.my_rotate_action, R.anim.alpha_scale, R.anim.alpha_rotate, R.anim.scale_rotate, R.anim.alpha_scale_rotate, R.anim.myown_design };
animation = AnimationUtils.loadAnimation(MainActivity.this, ID[randow]);//randow为随机取到0~7的数的随机数 textView.startAnimation(animation);
问题十:针对只有几个确定的数,使用SeekBar
打个比方,我只有10, 20, 30, 40, 50这几个数,我想使用SeekBar,最小值是10,最大值是50,如果我拖动进度条到95%的时候,刻度自动进到100% 即值为50的情况,那么在这种情况下我们该如何处理呢
请看下面的代码:
private class SeekBarListener implements SeekBar.OnSeekBarChangeListener { private TextView textView; private int TickStep; private int StartTick; public SeekBarListener(TextView tv, int startTick, int tickStep) { textView = tv; TickStep = tickStep; StartTick = startTick; } @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { // TODO Auto-generated method stub if (fromUser) { // .. } // 时间=process*步长+初始值 // int progress=seekBar.getProgress(); int curTick = progress + StartTick; int remainder = curTick % TickStep; int halfStep = TickStep % 2 == 0 ? TickStep - TickStep % 2 : TickStep - TickStep % 2 + 1; if (remainder < halfStep) { curTick -= remainder; } else { curTick += (TickStep - remainder); } // seekBar.setProgress(curTick - StartTick); textView.setText(curTick + "min"); } @Override public void onStartTrackingTouch(SeekBar seekBar) { // TODO Auto-generated method stub } @Override public void onStopTrackingTouch(SeekBar seekBar) { // TODO Auto-generated method stub // 时间=process*步长+初始值 int progress = seekBar.getProgress(); int curTick = progress + StartTick; int remainder = curTick % TickStep; int halfStep = TickStep % 2 == 0 ? TickStep - TickStep % 2 : TickStep - TickStep % 2 + 1; if (remainder < halfStep) { curTick -= remainder; } else { curTick += (TickStep - remainder); } seekBar.setProgress(curTick - StartTick); textView.setText(curTick + "min"); } }
番茄工作法APK下载:http://pan.baidu.com/s/1jGC4fXG
注:源代码已分享在GitHub上