第一份功能较多的安卓项目--纪念日app

因为正值跟女朋友在一起的半周年。所以想着做个她需要的app给她用。其实网上同样功能的app很多,但都有广告,而且比较庞大,她的手机又比较老卡,所以就根据她的需求来写,哈哈,算是我的第一个项目经理吧。
首先说一下她的需求:

  1. 倒数日的功能,能够显示某个日子离现在还有几天,提供自动排序的功能
  2. 纪念日的功能,能够显示某个日子已经过去了几天,提供两种显示方法,日跟月。
  3. 手电筒的功能
  4. 备注功能。即倒数日中能够添加备注(未实现)

接下来就说一下实现的过程:
首先是设计界面,先做好下xml,经验表明,边写功能边敲代码绝对是不对的,因为会很乱。设想有一个登陆界面,界面上是头像,有圆角的输入框(自定义),有圆角的按钮

pw_shape.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" 
    android:shape="rectangle"
    android:padding="10dp">
    <!--solid表示填充颜色,如果想让输入框看起来像透明的一样就让color跟背景颜色一样即可-->
    <solid
        android:color="#FFFFFF"></solid>
        <!--corners表示四个角的弧度,注意bottomRightRadius是左下角,left是右下角的弧度-->
    <corners
        android:bottomRightRadius="15dp"  
        android:bottomLeftRadius="15dp" 
        android:topLeftRadius="15dp"  
        android:topRightRadius="15dp" />
        <!--stroke表示描边,width表示宽度,color为描边的颜色-->
    <stroke
        android:width="2dp"
        android:color="#FF99CC"/>
        <!--padding:距离边缘的距离-->
    <padding
        android:left="10dp"/>  
</shape>

手电筒在登陆界面。手电筒的代码其实挺简单的。主要就是打开闪光灯。代码如下

lightBtn.setOnClickListener(new OnClickListener(){
            @Override
            public void onClick(View v){
                if(!flag){
                    layout.setBackgroundColor(Color.parseColor("#292421"));
                    lightBtn.setBackgroundResource(R.drawable.flash_head);
                    Camera camera = Camera.open();
                    parameters = camera.getParameters();
                    parameters.setFlashMode(Parameters.FLASH_MODE_TORCH);// 开启
                    camera.setParameters(parameters);
                    camera.startPreview();
                    flag=true;
                }else{
                    flag=false;
                    layout.setBackgroundColor(Color.parseColor("#FFFFFF"));
                    lightBtn.setBackgroundResource(R.drawable.log_in_head_2);
                     parameters.setFlashMode(Parameters.FLASH_MODE_OFF);// 关闭
                     camera.setParameters(parameters);
                     camera.stopPreview();
                     camera.release();
                     camera=null;
                }
            }
        });

开启的逻辑:

Camera camera = Camera.open();//打开Camera
Parameters parameters = camera.getParameters(); //获得参数          parameters.setFlashMode(Parameters.FLASH_MODE_TORCH);// 将闪光灯的模式设为开启
camera.setParameters(parameters);
camera.startPreview(); //开启闪关灯

关闭的逻辑:

layout.setBackgroundColor(Color.parseColor("#FFFFFF"));
                    lightBtn.setBackgroundResource(R.drawable.log_in_head_2);
                     parameters.setFlashMode(Parameters.FLASH_MODE_OFF);// 关闭
 camera.setParameters(parameters);
camera.stopPreview();
camera.release();
camera=null;//*

这里同上,只需要将parameter设为off,再stopPreview,再release即可。要注意的一点便是:release后要将camera设为null,否则关了再开会报错,提示camera已经被使用

之后便什么新东西,添加Button,EditText等。

然后便是纪念日的设计,同样先设计xml。因为女票要求简单,只需要界面简洁,所以就上方自己设计的普通的菜单,下面是个自定义的listView
自己定制的菜单:
其实就是一层layout。然后在里面放自己需要的东西。那我们一般的都是上面一横条,横条中间是标题,两边是按钮,新建一个LinearLayout。一次放入Button,TextView,Button.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/date"
     >

    <Button
        android:id="@+id/title_btn_back"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="返回"
        android:textColor="#fff"/>
    <!--要将TextView置中,则要将layout_width置0,layout_weight="1",这样会将一行分为两半,其中TextView占一半,另外两个button各占1/4--> 
    <TextView
        android:id="@+id/title_text"
        android:layout_width="0dip"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_weight="1"
        android:gravity="center"
        android:text="纪念日"
        android:textColor="#fff"
        android:textSize="24sp" />

<Button
        android:id="@+id/title_btn_add"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="新建"
        android:textColor="#fff"/>


</LinearLayout>

这个xml有两点要注意的,就是android:layout_gravity跟android:gravity的区别。
android:gravity是文字在控件中的对齐方式,而android:layout_gravity是控件在layout中的对齐方式。因为我们这个layout的高度设为wrap_content了,所以就只有按钮的高度。之后这个xml就可以被其他引用了。只需要将其他layout的标题栏隐藏,然后在xml中引入即可。隐藏标题栏的代码:

super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);//这一句一定要在setContentView前
setContentView(R.layout.activity_count_down);

然后便是listview的设计。因为我是想着左边一个纪念日的内容,然后右边一个框里面有天数,框的左边有”已经“二字,所以定制list_item.xml
首先将layout的orientation设为”horizontal”
然后将显示内容的TextView设为android:layout_weight=”1”。这样显示出来的效果就是“已经”二字贴着右边的框。
然后右边的框因为系统没有所以我们需要自己定制,其实也简单,就是一层有背景的layout,然后里面有一个TextView,设置其layout_gravity为center将其放在中间就可以了。
接着便是添加逻辑,主要是点击所在行可以进行编辑,长按弹出菜单可以删除,摇一摇可以让框3D旋转。
Adapter的重写,需要注意要把框所在的layout也申请个空间,因为字体是在这层layout上更改的。

class ViewHolder {
            private LinearLayout countDownLayout;
            private TextView content;
            private TextView date;
        }

        private DayAdapter(Context context, ArrayList<Day1> dates) {
            this.context = context;
            this.dates = dates;
        }

        @Override
        public int getCount() {
            // TODO Auto-generated method stub
            return dates.size();
        }

        @Override
        public Object getItem(int arg0) {
            // TODO Auto-generated method stub
            return dates.get(arg0);
        }

        @Override
        public long getItemId(int arg0) {
            // TODO Auto-generated method stub
            return arg0;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            // TODO Auto-generated method stub

            ViewHolder vh;
            View view;

            if (convertView == null) {
                vh = new ViewHolder();
                /**
                 *注意这里要先inflate自定义的list_item
                 */
                view = LayoutInflater.from(context).inflate(R.layout.list_item, null);
                /**
                 *注意这里要给layout一个空间
                 */
                vh.countDownLayout = (LinearLayout) view.findViewById(R.id.count_down_layout);
                vh.content = (TextView) view.findViewById(R.id.count_down_content);
                vh.date = (TextView) view.findViewById(R.id.count_down_datetext);
                view.setTag(vh);
            } else {
                view = convertView;
                vh = (ViewHolder) view.getTag();
            }
            vh.content.setText(dates.get(position).content);
            int passed = dates.get(position).days;
            if (first){
                vh.date.setText(passed + "天");
                first=false;
            }
            else {
                if (mark == 1) {
                    applyRotation(vh.countDownLayout, 0, 90);
                    vh.date.setText(passed + "天");
                } else {
                    applyRotation(vh.countDownLayout, 0, 90);
                    vh.date.setText(passed + "月");
                }
            }
            return view;
        }

    }

3D翻转特效:这个不懂,网上找的代码,需要翻转时调用applyRotation(layout,start,end)即可。一般把start设为0,end设为90,因为我们要翻转的是整个框,所以layout传入countDownLayout。因为我这个dayadapter中的list的泛型是Day1,其中有int型的一个数字,就是显示出来的天数。这样设计是为了能够让摇一摇时getView得以被调用(list中内容有变化,使用notifyDataSetChanged()才有效。)

private void applyRotation(LinearLayout layout, float start, float end) {
        final float centerX = layout.getWidth() / 2.0f;
        final float centerY = layout.getHeight() / 2.0f;
        final Rotate3dAnimation rotation = new Rotate3dAnimation(start, end, centerX, centerY, 310.f, true);
        rotation.setDuration(500);
        rotation.setFillAfter(true);
        rotation.setInterpolator(new AccelerateInterpolator());

        rotation.setAnimationListener(new DisplayNextView(layout));

        layout.startAnimation(rotation);
}

private final class DisplayNextView implements Animation.AnimationListener {
        private LinearLayout layout;

        DisplayNextView(LinearLayout layout) {
            this.layout = layout;
        }

        public void onAnimationStart(Animation animation) {

        }

        public void onAnimationRepeat(Animation animation) {

        }

        @Override
        public void onAnimationEnd(Animation arg0) {
            // TODO Auto-generated method stub
            layout.post(new SnapViews(layout));
        }
    }

    private final class SnapViews implements Runnable {
        private LinearLayout layout;

        SnapViews(LinearLayout layout) {
            this.layout = layout;
        }

        public void run() {
            final float centerX = layout.getWidth() / 2.0f;
            final float centerY = layout.getHeight() / 2.0f;
            Rotate3dAnimation rotation = null;

            layout.requestFocus();
            rotation = new Rotate3dAnimation(90, 0, centerX, centerY, 310.0f, false);
            rotation.setDuration(500);
            rotation.setInterpolator(new DecelerateInterpolator());
            layout.startAnimation(rotation);
        }
    }
}

之后是摇一摇。就是微信摇一摇的原理。使用手机的加速度传感器来感应是否有摇动。

private SensorManager sensorManager;
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
Sensor sensor =sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sensorManager.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_NORMAL);
/**
 *在onSensorChanged中添加代码逻辑,我设置两次摇一摇的时间不能超过4秒
 */
private SensorEventListener listener = new SensorEventListener() {
        @Override
        public void onSensorChanged(SensorEvent event) {
            float x = Math.abs(event.values[0]);
            float y = Math.abs(event.values[1]);
            float z = Math.abs(event.values[2]);
            if ((System.currentTimeMillis() - CountDownActivity.currentTime > 4000) && (x > 12 || y > 12 || z > 12)) {
                CountDownActivity.currentTime = System.currentTimeMillis();
                if (mark == 1) {
                    mark = 2;
                    for (Day1 d : dayLists) {
                        d.days = countDay(d.date, mark);
                        adapter.notifyDataSetChanged();
                    }
                } else if (mark == 2) {
                    mark = 1;
                    for (Day1 d : dayLists) {
                        d.days = countDay(d.date, mark);
                    }
                    adapter.notifyDataSetChanged();
                }
            }
        }

        @Override
        public void onAccuracyChanged(Sensor sensor, int accuracy) {

        }
    };

弹出菜单:首先给菜单注册,然后重写代码即可。add方法的四个参数含义:
menu.add(groupId, itemId, order, title)

groupld 这个菜单的组别

itemid 是用来获取这个指定菜单项的.可以定义常量,不容易混,比如final int DELETE=1;

所谓order就是这个组别的第几项,0为第一项

title 不用说都知道是显示的标题了

registerForContextMenu(list);
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
        menu.add(0, 1, 0, "删除");
}

@Override
    public boolean onContextItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case 1:
            Day1 d = (Day1) adapter.getItem(clickId);
            Log.e("clickId", " " + clickId);
            String content = d.content;
            for (Day1 d1 : dayLists) {
                if (d1.content.equals(content)) {
                    dayLists.remove(d1);
                    break;
                }
            }
            SQLiteDatabase db = dbHelper.getWritableDatabase();
            db.delete("CountDays", "content=?", new String[] { d.content });
            adapter.notifyDataSetChanged();
        }
        return true;
    }

至于添加详情的代码则没什么好写的了。界面也没有设计。

猜你喜欢

转载自blog.csdn.net/BenjaminYoung29/article/details/51553211