PopupWindow全屏显示以及适配不同手机屏幕之 应用实例 更换头像,拍照,相册选取附带动画效果

本文介绍了一个使用PopupWindow实现更换头像功能的应用实例,包括拍照和从相册选取照片,同时涉及PopupWindow在不同手机屏幕上的全屏显示和位置调整。通过注释详细代码,讲解了如何解决PopupWindow显示位置和全屏适配问题,以及状态栏高度计算等关键点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近找工作,所以闲余时间还是比较充足的,今晚刚好没有睡意,抽取了一个之前项目中自己写过的一个功能。更换头像,可以拍照,可以从相册选取照片,有裁剪功能。其中的一个坑是PopupWindow的显示位置以及是否全屏显示适配不同手机。
话不多少,先上效果。

这里写图片描述 这里写图片描述
因为这个效果展示时间比较长,所以分了两段录结果还是大了,把gif图片质量压缩了。
大致解释下,效果就是点击按钮弹出popupwindow,更换头像可选择拍照和相册选取,并且可以裁剪。退出程序保存的头像依然保留。

下来先看看一步一步实现
由于今天我的Android studio有点,所以这个demo我是用eclipse写的。效果都是一样的。
1.PopupWindow 首先是布局样式 activity_choose_picture.xml

<RelativeLayout 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"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_margin="10dp"
        android:background="@color/btn_bottom_tv_gray"
        android:orientation="vertical" >

        <Button
            android:id="@+id/button_take_photo"
            style="@style/txt_camera_pop_menu"
            android:layout_width="match_parent"
            android:layout_height="45dp"
            android:background="@drawable/pop_first_selector"
            android:text="@string/camera_pop_camera"
            android:textSize="18sp" />

        <Button
            android:id="@+id/button_choice_photo"
            style="@style/txt_camera_pop_menu"
            android:layout_width="match_parent"
            android:layout_height="45dp"
            android:background="@drawable/pop_last_selector"
            android:text="@string/camera_pop_album"
            android:textSize="18sp" />

        <Button
            android:id="@+id/button_choice_cancel"
            style="@style/txt_camera_pop_menu"
            android:layout_width="match_parent"
            android:layout_height="45dp"
            android:layout_marginTop="10dp"
            android:background="@drawable/pop_single_selector"
            android:text="@string/camera_pop_cancel"
            android:textSize="18sp" />
    </LinearLayout>

</RelativeLayout>

这个就是弹出popupwindow的布局
资源文件,style.xml这里我们要自定义样式

    <!-- Application theme. -->
    <style name="AppTheme" parent="AppBaseTheme">
        <!-- All customizations that are NOT specific to a particular API-level can go here. -->
         <item name="android:windowNoTitle">true</item>
    </style>



     <style name="PopupAnimation" parent="android:Animation">
        <item name="android:windowEnterAnimation">@anim/push_bottom_in</item>
        <item name="android:windowExitAnimation">@anim/push_bottom_out</item>
    </style>

     <style name="txt_camera_pop_menu">
        <!-- PopupWindow左右弹出的效果 -->
        <item name="android:textColor">@color/camera_pop_normal</item>
        <item name="android:textSize">16sp</item>
        <item name="android:paddingTop">10dp</item>
        <item name="android:paddingBottom">10dp</item>
        <item name="android:gravity">center</item>
    </style>

还有两个动画xml文件 push_bottom_in.xml

<?xml version="1.0" encoding="utf-8"?>
<!-- 上下滑入式 -->
<set xmlns:android="http://schemas.android.com/apk/res/android" >

    <translate
        android:duration="1000"
        android:fromYDelta="100%p"
        android:toYDelta="0" />

    <alpha
        android:duration="1000"
        android:fromAlpha="0.0"
        android:toAlpha="1.0" />

</set>

push_bottom_out.xml

<?xml version="1.0" encoding="utf-8"?>
<!-- 上下滑入式 -->
<set xmlns:android="http://schemas.android.com/apk/res/android" >

    <translate
        android:duration="1000"
        android:fromYDelta="0"
        android:toYDelta="50%p" />

    <alpha
        android:duration="1000"
        android:fromAlpha="1.0"
        android:toAlpha="0.0" />

</set>

需要注意的是这两个文件是建在anim目录下的,否则不起作用。
下来主要看MainActivity中的代码,
代码我注释写的比较详细,就直接看吧

package com.feilong.popupwindow;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.lang.reflect.Field;

import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.ColorDrawable;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
import android.view.WindowManager;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.PopupWindow;
import android.widget.PopupWindow.OnDismissListener;

public class MainActivity extends Activity {
    private Button button;
    private ImageView mIvHead;//更换的图片

    private View mGoneView;// 参照物
    private PopupWindow mPopupWindow; // popwindow
    private View mpopview; // 弹出框的布局
    private Bitmap photo; // 保存头像的Bitmap对象
    private static final int RESULT_PICK_PHOTO_CAMERA = 1; // 退出
    private static int CAMERA_RESULT = 100; // 拍照 标识码
    private static int LOAD_IMAGE_RESULT = 200; // 相册 标识码

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button = (Button) findViewById(R.id.button1);
        mGoneView = findViewById(R.id.gone_view);
        mIvHead = (ImageView) findViewById(R.id.image_head);

        photo = FileUtill.readerByteArrayToSD(); // 读取保存的图片
        if (photo != null) {
            mIvHead.setImageBitmap(photo);
        } else {
            mIvHead.setImageDrawable(getResources().getDrawable(R.drawable.ic_launcher));
        }
        button.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View view) {
                // TODO Auto-generated method stub
                showPopupWindon();
            }
        });
    }

    /**
     * 弹出PopupWindow
     */
    private void showPopupWindon() {
        int width = getResources().getDisplayMetrics().widthPixels;
        int height = getResources().getDisplayMetrics().heightPixels;// px
        LayoutInflater inflater = LayoutInflater.from(this);
        mpopview = inflater.inflate(R.layout.activity_choose_picture, null);// 加载动画布局
        mPopupWindow = new PopupWindow(mpopview, width, height - dip2px(50)
                + getStatusBarHeight());// 设置布局在屏幕中显示的位置,并且获取焦点
        // 设置PopupWindow的显示样式
        mPopupWindow.setAnimationStyle(R.style.PopupAnimation);
        // 实例化一个ColorDrawable颜色为半透明
        ColorDrawable dw = new ColorDrawable(0x00000000);
        // 设置SelectPicPopupWindow弹出窗体的背景
        mPopupWindow.setBackgroundDrawable(dw);
        backgroundAlpha(this, 0.5f);// 设置半透明0.0-1.0
        mPopupWindow.setFocusable(true);
        mPopupWindow.setOutsideTouchable(false);// 设置不允许在外点击消失
        // 设置当mPopupWindow取消时,界面恢复原来的颜色 不是可透明的
        mPopupWindow.setOnDismissListener(new OnDismissListener() {
            @Override
            public void onDismiss() {
                // TODO Auto-generated method stub
                backgroundAlpha(MainActivity.this, 1f);// 不透明
            }
        });

        mPopupWindow.showAsDropDown(mGoneView);// 弹出的mPopupWindow左上角正对mGoneView的左下角
                                                // 偏移量默认为0,0

        Button mTakePhotoBt = (Button) mpopview
                .findViewById(R.id.button_take_photo);// 拍照
        Button mChoicePhotoBt = (Button) mpopview
                .findViewById(R.id.button_choice_photo);// 相册选择
        Button mChoiceCancelBt = (Button) mpopview
                .findViewById(R.id.button_choice_cancel);// 取消
        // 拍照
        mTakePhotoBt.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                mPopupWindow.dismiss();
                // destoryImage();
                File saveDirFile = new File(Environment
                        .getExternalStorageDirectory(), "" + "temp.jpg");
                Intent intentCamera = new Intent(
                        MediaStore.ACTION_IMAGE_CAPTURE);// 调用系统相机拍照
                intentCamera.putExtra(MediaStore.EXTRA_OUTPUT,
                        Uri.fromFile(saveDirFile));
                startActivityForResult(intentCamera, CAMERA_RESULT);
            }
        });
        // 从相册选择
        mChoicePhotoBt.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                mPopupWindow.dismiss();// 取消弹窗
                // content://media/external/images/media 图片地址
                // Intent.ACTION_PICK 从图片中选择一张 并返回选择的图片
                Intent intentPhotoAlbum = new Intent(
                        Intent.ACTION_PICK,
                        android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
                startActivityForResult(intentPhotoAlbum, LOAD_IMAGE_RESULT);// 请求码200
                                                                            // 打开相册
            }
        });
        // 取消
        mChoiceCancelBt.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                mPopupWindow.dismiss();
            }
        });
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        // 拍照
        if (requestCode == CAMERA_RESULT && resultCode == RESULT_OK) {
            File temp = new File(Environment.getExternalStorageDirectory()
                    + "/" + "" + "temp.jpg");
            startPhotoZoom(Uri.fromFile(temp));
        }
        // 相册选择
        if (requestCode == LOAD_IMAGE_RESULT && data != null
                && data.getData() != null) {
            // Uri selectedImage = data.getData();
            startPhotoZoom(data.getData());
        }
        // 取消
        if (requestCode == RESULT_PICK_PHOTO_CAMERA && data != null
                && data.getExtras() != null) {
            Bundle extras = data.getExtras();
            Bitmap photo = extras.getParcelable("data");
            mIvHead.setImageBitmap(photo);
            ByteArrayOutputStream stream = new ByteArrayOutputStream();
            photo.compress(Bitmap.CompressFormat.JPEG, 60, stream);
            byte[] bytes = stream.toByteArray();
            mIvHead.setTag(bytes);
            String filePath = Environment.getExternalStorageDirectory() + "/"
                    + "" + "temp.jpg";
            FileUtill.writeByteArrayToSD(filePath, bytes, true);
        }
    }

    /**
     * 设置添加屏幕的背景透明度
     * 
     * @param context
     * @param bgAlpha
     *            (透明度 取值返回0-1, 0全透明,1不透明)
     * @date 2016年8月6日
     */
    public void backgroundAlpha(Activity context, float bgAlpha) {

        WindowManager.LayoutParams lp = context.getWindow().getAttributes();
        lp.alpha = bgAlpha;
        context.getWindow()
                .addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
        context.getWindow().setAttributes(lp);
    }

    /**
     * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
     */
    private int dip2px(float dpValue) {
        final float scale = getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    /**
     * 裁剪图片方法实现
     */
    @SuppressLint("NewApi")
    public void startPhotoZoom(Uri uri) {
        Intent intent = new Intent("com.android.camera.action.CROP");
        intent.setDataAndType(uri, "image/*");
        // 下面这个crop=true是设置在开启的Intent中设置显示的VIEW可裁剪
        intent.putExtra("crop", "true");
        // aspectX aspectY 是宽高的比例
        intent.putExtra("aspectX", 1);
        intent.putExtra("aspectY", 1); // outputX outputY 是裁剪图片宽高
        intent.putExtra("outputX", 200);
        intent.putExtra("outputY", 200);
        intent.putExtra("scale", true);// 黑边
        intent.putExtra("scaleUpIfNeeded", true);// 黑边
        intent.putExtra("return-data", true);
        startActivityForResult(intent, RESULT_PICK_PHOTO_CAMERA);
    }

    /**
     * 获取状态栏高速的方法
     * 
     * @return
     * @date 2016年8月7日
     */
    private int getStatusBarHeight() {
        Class<?> c = null;
        Object obj = null;
        Field field = null;
        int x = 0, sbar = 0;
        try {
            c = Class.forName("com.android.internal.R$dimen");
            obj = c.newInstance();
            field = c.getField("status_bar_height");
            x = Integer.parseInt(field.get(obj).toString());
            sbar = getResources().getDimensionPixelSize(x);
        } catch (Exception e1) {
            e1.printStackTrace();
        }

        return sbar;
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

}

这里有几点需要注意下,1,showAsDropDown(mGoneView);这个方法,是控制popupwindow显示位置的,参数我们可以理解为一个相对参照物,它是指// 弹出的PopupWindow左上角正对mGoneView的左下角显示。难怪之前没有设置的时候怎么调宽高都不是全屏显示的。这里的mGoneView是在activity_main.xml的最上面画一条线长宽为0,状态是gone。
2,mPopupWindow = new PopupWindow(mpopview, width, height - dip2px(50)
+ getStatusBarHeight());// 设置布局在屏幕中显示的位置,并且获取焦点
这行代码主要看第三个参数。其实测试发现Android系统有的PopupWindow计算高度的时候没有计算状态栏的高度,导致不能全屏。getStatusBarHeight()方法是获取状态栏高度的,至于这个为什么要减去dip2px(50)也不是很懂,他是一个把dp转化为px的一个方法。如果不减去则有的手机下面的“取消”会看不见。

photo = FileUtill.readerByteArrayToSD(); // 读取保存的图片
        if (photo != null) {
            mIvHead.setImageBitmap(photo);
        } else {
            mIvHead.setImageDrawable(getResources().getDrawable(R.drawable.ic_launcher));
        }

之前将照片保存在手机中,以后每当打开程序它会从sd卡中读取之前保存的头像照片,并且设置显示,如果没有的话,则默认显示ic_launcher。
今天时间有点晚了,就不在细说了,稍后我会附上

源码

,大家有兴趣可以下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值