高仿微信摇一摇功能

网上也有一些仿微信摇一摇的,但大部分都不完整。今天就自己动手来仿一下。有图的当然先上一下图:

                                             
触发摇一摇后手机还有音效和震动效果,gif展示不出来。另外在设置界面还能更改背景图,设置音效的开关,点击更换后打开一个图片选择器,这里不会讲这个选择器,有兴趣的可以参照鸿神的博客,我的大部分也是参照那上面来的,链接:http://blog.csdn.net/lmj623565791/article/details/39943731。
1. 摇一摇界面

首先看一下界面,上面的标题栏没什么好说的,我用的是toolbar。中间应该有两个ImageView,触发摇一摇后应该有个动画,两个imageview分别向上下运动,显示出隐藏的图片,结束后再返回。所以中间应该还有个隐藏的imageview,而且以那两个imageview为边界并且完全被遮挡住。下面可以用三个linearlayout,每个都包含图片和文字,点击后图片变色并且改变标题内容,布局文件如下:



    

    

    

    

    

        

            

            
        

        

            

            
        

        

            

            
        
    

其中的图片资源我都提前准备好了。注意一下中间的三个imageview的位置是还没确定的,需要在代码中测得控件高度后进行偏移后才能完成,而在onCreate方法中直接测量控件高度是为0的,因为此时控件还没进行测量绘制。可以用过一个post方法执行,这样能保证控件已测量完成:

private void initView(){
//        …初始化控件
        imageHidden.post(new Runnable() {
    @Override
    public void run() {
        int imageHeight = imageDown.getHeight();
        imageUp.setTranslationY(-imageHeight/2);
        imageDown.setTranslationY(imageHeight/2);
        ViewGroup.LayoutParams params = imageHidden.getLayoutParams();
        params.height = imageHeight*2;
        imageHidden.setLayoutParams(params);
    }
});

这样才能让上下两张图片刚好位于中间,而隐藏的图片也刚好被遮住。
2.摇一摇效果
摇一摇是通过手机的运动来判定的,需要使用加速度传感器。控制传感器的类是Sensor,在onCreate方法中初始化,分别在onResume和onPause中注册和注销,注册时需要传入监听器,当传感器测得数值发生改变时会回调onSensorChanged方法,在该方法内获得加速度并进行判断,如果大于阈值则触发摇一摇。摇一摇的效果包括三个,两个imageview的动画,手机震动以及手机音效。相关的代码如下:

public class ShakeActivity extends AppCompatActivity implements View.OnClickListener,SensorEventListener{
    private static final int minValue = 15;
    private static final long[] vibratePattern = {100,200,100,200};
//    -1表示只震动一次,非-1表示从pattern的指定下标开始重复振动
    private static final int vibrateRepeat = -1;
//    …控件的声明
    private SensorManager sensorManager;
    private Sensor vibrateSensor;
//    实现震动的类
    private Vibrator vibrator;
//    上面的动画
    private Animation bounceUpAnimation;
//    下面的动画
    private Animation bounceDownAnimation;
//    是否允许摇动,用来防止短时间内连续触发事件
    private boolean allowShake = true;
//    产生音效的类
    private SoundPool mSoundPool;
//    音效的ID
    private int soundId;
//    是否开启音效
    private boolean useSound;
//    背景图
    private String currentImage;
//    用于在子线程加载完毕后更新背景图
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            Bitmap bitmap = (Bitmap) msg.obj;
            imageHidden.setImageBitmap(bitmap);
        }
    };
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_shake);
        initView();
        setupAnimation();
//        默认的背景图
        currentImage = ShakeSettingActivity.DEFAULT_IMAGE;
    }

    private void setupAnimation() {
        bounceUpAnimation = AnimationUtils.loadAnimation(this,R.anim.translate_up_down);
        bounceDownAnimation = AnimationUtils.loadAnimation(this,R.anim.translate_down_up);
        bounceUpAnimation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                allowShake = false;
                vibrator.vibrate(vibratePattern, vibrateRepeat);
                if(useSound){
                    mSoundPool.play(soundId,1,1,0,0,1);
                }
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                allowShake = true;
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });
    }

    @Override
    protected void onResume() {
        super.onResume();
        sensorManager.registerListener(this, vibrateSensor,SensorManager.SENSOR_DELAY_NORMAL);
        soundId = mSoundPool.load(this,R.raw.shake_sound_male,0);
        SharedPreferences preferences = getSharedPreferences(ShakeSettingActivity.PREF_NAME,MODE_PRIVATE);
        useSound = preferences.getBoolean(ShakeSettingActivity.PREF_KEY_USE_SOUND,true);
        final String imageUrl = preferences.getString(ShakeSettingActivity.PREF_KEY_BACKGROUND,ShakeSettingActivity.DEFAULT_IMAGE);
        if(imageUrl != currentImage){
//            如果图片不同则进行更新
            currentImage = imageUrl;
            if(imageUrl.equals(ShakeSettingActivity.DEFAULT_IMAGE)){
//                重置为默认图片
                imageHidden.setImageResource(R.drawable.a5z);
                return;
            }
            new Thread(new Runnable() {
                @Override
                public void run() {
//                    开启一个线程加载图片
                    Bitmap bitmap = BitmapFactory.decodeFile(imageUrl);
                    Message message = Message.obtain();
                    message.obj = bitmap;
                    mHandler.sendMessage(message);
                }
            }).start();
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        sensorManager.unregisterListener(this);
        mSoundPool.release();
    }

    private void initView() {
//        …初始化控件
        sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        vibrateSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
        mSoundPool = new SoundPool(5, AudioManager.STREAM_MUSIC,0);
        imageHidden.post(new Runnable() {
            @Override
            public void run() {
                int imageHeight = imageDown.getHeight();
                imageUp.setTranslationY(-imageHeight/2);
                imageDown.setTranslationY(imageHeight/2);
                ViewGroup.LayoutParams params = imageHidden.getLayoutParams();
                params.height = imageHeight*2;
                imageHidden.setLayoutParams(params);
            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_shake,menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()){
            case R.id.menu_item_setting:
                startActivity(new Intent(this,ShakeSettingActivity.class));
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onClick(View v) {
        clearState();
        switch (v.getId()){
            case R.id.ll_shake_people:
                toolbar.setTitle("摇一摇");
                viewPeople.setSelected(true);
                break;
            case R.id.ll_shake_music:
                toolbar.setTitle("摇歌曲");
                viewMusic.setSelected(true);
                break;
            case R.id.ll_shake_tv:
                toolbar.setTitle("摇电视");
                viewTv.setSelected(true);
                break;
        }
    }

    private void clearState() {
        viewPeople.setSelected(false);
        viewMusic.setSelected(false);
        viewTv.setSelected(false);
    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        if(!allowShake){
            return;
        }
        float[] values = event.values;
//        z轴减去重力加速度9.8
        if(Math.abs(values[0])>minValue||Math.abs(values[1])>minValue||Math.abs(values[2]-9.8)>minValue){
            imageUp.startAnimation(bounceUpAnimation);
            imageDown.startAnimation(AnimationUtils.loadAnimation(this,R.anim.translate_down_up));
        }
    }

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

    }
}

其中手机震动使用的是Vibrator,音效使用的是SoundPool,使用都不难,看文档即可。背景图的URL、是否使用音效保存在sharedPreference内,onResume中获取并进行更新。图片的加载开启了一个子线程,避免阻碍UI线程,加载完毕后再handler中对隐藏图片进行更新。


3. 摇一摇设置
摇一摇的设置界面主要提供三个功能:使用默认背景图片,更换背景图片以及开启或关闭音效,值都是保存再sharedPreference中。点击更换背景图时打开图片选择器,选择图片后返回图片的URL并进行保存。主要的代码如下:

public class ShakeSettingActivity extends AppCompatActivity implements View.OnClickListener{

//    sharedPreference文件中保存是否使用音效的KEY
    public static final String PREF_KEY_USE_SOUND = "shake_use_sound";
//    sharedPreference文件中保存图片url的KEY
    public static final String PREF_KEY_BACKGROUND = "shake_background";
//    sharedPreference文件名
    public static final String PREF_NAME = "Setting";
//    用于表示默认图片
    public static final String DEFAULT_IMAGE = "default_image";
    private static final int CODE_SELECT_IMAGE = 0;
    private TextView tvSetDefaultImage;
    private TextView tvChangeImage;
    private TextView tvPeople;
    private TextView tvMessage;
    private TextView tvHistory;
    private ToggleButton tgBtnSound;
    private boolean useSound;
    private SharedPreferences mSharedPreferences;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_shake_setting);
        initView();
        initData();
    }

    @Override
    protected void onPause() {
        super.onPause();
        if(tgBtnSound.isSelected() != useSound){
//            不相同说明设置发生了变化,需要写回sharedPreference文件
            mSharedPreferences.edit().putBoolean(PREF_KEY_USE_SOUND,useSound).commit();
        }
    }

    private void initData() {
        mSharedPreferences = getSharedPreferences(PREF_NAME,MODE_PRIVATE);
//        获取mSharedPreferences中保存的值
        useSound = mSharedPreferences.getBoolean(PREF_KEY_USE_SOUND,true);
        tgBtnSound.setSelected(useSound);
    }

    private void initView() {
        Toolbar toolbar = (Toolbar) findViewById(R.id.tool_bar);
        toolbar.setTitle("摇一摇设置");
        toolbar.setNavigationIcon(R.drawable.actionbar_icon_back);
        setSupportActionBar(toolbar);
        tvSetDefaultImage = (TextView) findViewById(R.id.tv_shakeSetting_setDefaultImage);
        tvChangeImage = (TextView) findViewById(R.id.tv_shakeSetting_changeImage);
        tvPeople = (TextView) findViewById(R.id.tv_shakeSetting_people);
        tvMessage = (TextView) findViewById(R.id.tv_shakeSetting_message);
        tvHistory = (TextView) findViewById(R.id.tv_shakeSetting_history);
        tgBtnSound = (ToggleButton) findViewById(R.id.tgBtn_sound);
        tvSetDefaultImage.setOnClickListener(this);
        tvChangeImage.setOnClickListener(this);
        tvPeople.setOnClickListener(this);
        tvMessage.setOnClickListener(this);
        tvHistory.setOnClickListener(this);
        tgBtnSound.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.tv_shakeSetting_setDefaultImage:
                saveBackground(DEFAULT_IMAGE);
                Toast.makeText(this,"已恢复默认背景",Toast.LENGTH_SHORT).show();
                break;
            case R.id.tv_shakeSetting_changeImage:
                Intent intent = new Intent(this,ChooseImageActivity.class);
                intent.putExtra(ChooseImageActivity.KEY_SELECT_MODE,ChooseImageActivity.MODE_SINGLE);
                startActivityForResult(intent, CODE_SELECT_IMAGE);
                break;
            case R.id.tgBtn_sound:
                tgBtnSound.setSelected(!tgBtnSound.isSelected());
                break;
            default:
                Toast.makeText(this,"有待开发",Toast.LENGTH_SHORT).show();
        }
    }

    private void saveBackground(String image) {
        mSharedPreferences.edit()
                .putString(PREF_KEY_BACKGROUND,image)
                .commit();
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode){
            case CODE_SELECT_IMAGE:
                if(resultCode == RESULT_OK){
//                    结果正常,取得照片的URL并保存
                    String[] image = data.getStringArrayExtra(ChooseImageActivity.KEY_CONTENT);
                    saveBackground(image[0]);
                }
                break;
        }
    }
}

这样就大体上完成了微信的摇一摇功能了~

你可能感兴趣的:(Android开发,android,ui,微信,界面,控件)