在项目开发的过程中,产品提出了一个类似网易和今日头条不感兴趣的那种弹窗,感觉效果还不错,网上相关的例子又没找到,为了让小伙伴们少加点班特意封装了一下供给大家使用。那个小尖角用的是贝塞尔曲线,如有更好的实现方法,请留言
===========================================================================================
版权所有,如需转载请标明出处:http://blog.csdn.net/you4580
=============================================================
FitPopupWindowLayout.class
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.widget.RelativeLayout;
/**
* Created by you4580 on 2018/4/21.
* <p>主要处理布局逻辑
* 气泡
*/
public class FitPopupWindowLayout extends RelativeLayout {
public static final int UP = 1;
public static final int RIGHT = 2;
public static final int LEFT = 3;
public static final int DOWN = 4;
private int mHorizontal = LEFT;
private int mVertical = DOWN;
private Paint mPaint;
public static final int SHARP_WIDTH = 50;
public static final int SHARP_HEIGHT = (int) (SHARP_WIDTH * 1.0f);
private static final int RECT_CORNER = 10;
private int mXoffset = 20;
private Path mPath = new Path();
private Path mSharpPath = new Path();
public FitPopupWindowLayout(Context context) {
this(context, null);
}
public FitPopupWindowLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FitPopupWindowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setBackgroundColor(Color.TRANSPARENT);
mPaint = new Paint();
mPaint.setColor(Color.WHITE);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setAntiAlias(true);
}
private Path makeSharpPath() {
mSharpPath.moveTo(mXoffset, getMeasuredHeight() - SHARP_HEIGHT);
mSharpPath.cubicTo(mXoffset, getMeasuredHeight(), mXoffset, getMeasuredHeight() - SHARP_HEIGHT,
SHARP_WIDTH + mXoffset, getMeasuredHeight() - SHARP_HEIGHT);
return mSharpPath;
}
@Override
protected void onDraw(Canvas canvas) {
mPath.addRoundRect(new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight() - SHARP_HEIGHT)
, RECT_CORNER, RECT_CORNER, Path.Direction.CW);
mPath.addPath(makeSharpPath());
canvas.drawPath(mPath, mPaint);
if (mHorizontal == LEFT && mVertical == UP) {
setScaleX(1);
setScaleY(1);
} else if (mHorizontal == LEFT && mVertical == DOWN) {
setScaleX(1);
setScaleY(-1);
scaleChild(1, -1);
} else if (mHorizontal == RIGHT && mVertical == UP) {
setScaleX(-1);
setScaleY(1);
scaleChild(-1, 1);
} else if (mHorizontal == RIGHT && mVertical == DOWN) {
setScaleX(-1);
setScaleY(-1);
scaleChild(-1, -1);
}
}
private void scaleChild(float scaleX, float scaleY) {
for (int i = 0; i < getChildCount(); i++) {
getChildAt(i).setScaleX(scaleX);
getChildAt(i).setScaleY(scaleY);
}
}
public void setOrientation(int horizontal, int vertical, int xOffset) {
mHorizontal = horizontal;
mVertical = vertical;
mXoffset = xOffset;
invalidate();
}
}
=====================================================
FitPopupWindow.class
/** * Created by you4580 on 2018/4/21.
*主要处理popWindow弹出位置的逻辑 */
public class FitPopupWindow extends PopupWindow implements PopupWindow.OnDismissListener {
private View anchorView;
private Activity context;
private int mWindowWidth;
private static final int PADDING = 0;
//x轴坐标
private int mXCoordinate;
private int mHorizontal;
private int mVertical;
private int[] windowPos;
private FitPopupWindowLayout mFitPopupWindowLayout;
public FitPopupWindow(Activity context) {
init(context, ViewGroup.LayoutParams.WRAP_CONTENT
, ViewGroup.LayoutParams.WRAP_CONTENT);
}
public FitPopupWindow(Activity context, int width, int height) {
mWindowWidth = width;
init(context, width, height);
}
private void init(Activity context, int width, int height) {
this.context = context;
//popupwindow会默认忽略最外层的大小,所以应该再嵌套一层
setWidth(width);
setHeight(height);
setBackgroundDrawable(new ColorDrawable(0x00000000));
setOutsideTouchable(true);
setFocusable(true);
setOnDismissListener(this);
setAnimationStyle(R.style.popp_anim);
}
public void setView(View contentView, View anchorView) {
this.anchorView = anchorView;
windowPos = calculatePopWindowPos(anchorView, contentView);
mFitPopupWindowLayout = new FitPopupWindowLayout(context);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, getHeight() - FitPopupWindowLayout.SHARP_HEIGHT);
layoutParams.bottomMargin = FitPopupWindowLayout.SHARP_HEIGHT;
contentView.setLayoutParams(layoutParams);
mFitPopupWindowLayout.setOrientation(getHorizontal(), getVertical()
, getXCoordinate());
mFitPopupWindowLayout.addView(contentView);
setContentView(mFitPopupWindowLayout);
}
public void show() {
showAtLocation(anchorView, Gravity.TOP | Gravity.END
, windowPos[0], windowPos[1]);
update();
Window window = context.getWindow();
WindowManager.LayoutParams lp = window.getAttributes();
lp.alpha = 0.7f;
window.setAttributes(lp);
}
/**
* @param anchorView 弹出window的view
* @param contentView window的内容布局
* @return window显示的左上角的xOff, yOff坐标
*/
protected int[] calculatePopWindowPos(final View anchorView, final View contentView) {
final int windowPos[] = new int[2];
final int anchorLoc[] = new int[2];
// 获取锚点View在屏幕上的左上角坐标位置
anchorView.getLocationOnScreen(anchorLoc);
final int anchorHeight = anchorView.getHeight();
final int anchorWidth = anchorView.getWidth();
mXCoordinate = anchorLoc[0];
// 获取屏幕的高宽
final int screenHeight = Constants.screenHeight;
final int screenWidth = Constants.screenWidth;
contentView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
// 计算contentView的高宽
int windowHeight = contentView.getMeasuredHeight();
mWindowWidth = mWindowWidth > 0 ? mWindowWidth : contentView.getMeasuredWidth();
// 判断需要向上弹出还是向下弹出,如果要改变弹出策略,改变此处即可
// 目前是根据屏幕的一半进行判断
final boolean isNeedShowUp = (screenHeight - anchorLoc[1] - anchorHeight < screenHeight / 2);
// 判断需要向左弹出还是向右弹出
final boolean isNeedShowLeft = (anchorLoc[0] < mWindowWidth / 2);
setHorizontal(isNeedShowLeft ? FitPopupWindowLayout.LEFT : FitPopupWindowLayout.RIGHT);
setVertical(isNeedShowUp ? FitPopupWindowLayout.UP : FitPopupWindowLayout.DOWN);
// windowPos[0] = isNeedShowLeft ?
// anchorLoc[0] - windowWidth : anchorLoc[0] + anchorWidth;
windowPos[0] = (screenWidth - mWindowWidth) / 2;
windowPos[1] = isNeedShowUp ?
anchorLoc[1] - windowHeight - PADDING - FitPopupWindowLayout.SHARP_HEIGHT
: anchorLoc[1] + anchorHeight + PADDING;
return windowPos;
}
@Override
public void dismiss() {
super.dismiss();
}
@Override
public void onDismiss() {
WindowManager.LayoutParams lp = context.getWindow().getAttributes();
lp.alpha = 1f;
context.getWindow().setAttributes(lp);
}
public int getXCoordinate() {
if (mXCoordinate > mWindowWidth / 2) {
mXCoordinate = mWindowWidth - mXCoordinate - anchorView.getWidth() + 80;
}
return mXCoordinate;
}
private int getHorizontal() {
return mHorizontal;
}
/**
* @param mHorizontal 设置水平方向
*/
private void setHorizontal(int mHorizontal) {
this.mHorizontal = mHorizontal;
}
private int getVertical() {
return mVertical;
}
/**
* @param mVertical 设置竖直方向
*/
private void setVertical(int mVertical) {
this.mVertical = mVertical;
}
private void startAnimation(boolean isStart) {
AnimationSet animationSet = new AnimationSet(true);
ScaleAnimation sa;
sa = new ScaleAnimation(0, 1f, 0, 1f,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
sa.setDuration(150);
sa.setInterpolator(new FastOutSlowInInterpolator());
sa.setFillAfter(true);
AlphaAnimation aa = new AlphaAnimation(0f, 1f);
aa.setDuration(150);
aa.setFillAfter(true);
animationSet.addAnimation(sa);
animationSet.addAnimation(aa);
mFitPopupWindowLayout.startAnimation(animationSet);
}
}
===============================================================
/**
* Created by you4580 on 2018/4/21.用户操作的一些逻辑处理
*/
public class FitPopupUtil implements View.OnClickListener {
private View contentView;
private Activity context;
private LinearLayout reason_layout_1;
private TextView reason1;
private TextView reason2;
private LinearLayout reason_layout_2;
private TextView reason3;
private TextView reason4;
private TextView btnCommit;
private boolean reason1Selected;
private boolean reason2Selected;
private boolean reason3Selected;
private boolean reason4Selected;
private FitPopupWindow mPopupWindow;
private OnCommitClickListener listener;
public FitPopupUtil(Activity context,String reasons) {
this.context = context;
LayoutInflater inflater = LayoutInflater.from(context);
contentView = inflater.inflate(R.layout.layout_no_interest_popupwindow, null);
reason1 = (TextView) contentView.findViewById(R.id.tv_reason1);
reason2 = (TextView) contentView.findViewById(R.id.tv_reason2);
reason3 = (TextView) contentView.findViewById(R.id.tv_reason3);
reason4 = (TextView) contentView.findViewById(R.id.tv_reason4);
reason_layout_2 = (LinearLayout) contentView.findViewById(R.id.reason_layout_2);
btnCommit = (TextView) contentView.findViewById(R.id.btn_commit);
reason1.setOnClickListener(this);
reason2.setOnClickListener(this);
reason3.setOnClickListener(this);
reason4.setOnClickListener(this);
setReasons(reasons);
}
public void setOnClickListener(OnCommitClickListener listener) {
this.listener = listener;
}
/**
* 弹出自适应位置的popupwindow
*
* @param anchorView 目标view
*/
public View showPopup(View anchorView) {
if (mPopupWindow == null) {
DensityUtil densityUtil = new DensityUtil(context);
mPopupWindow = new FitPopupWindow(context,
Constants.screenWidth - densityUtil.dip2px(20),
ViewGroup.LayoutParams.WRAP_CONTENT
);
}
mPopupWindow.setView(contentView, anchorView);
mPopupWindow.show();
return contentView;
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.tv_reason1:
if(!reason1Selected){
MobclickAgent.onEvent(context,"zixun_toutiao_hate_detail", "zixun_toutiao_hate_detail_content");
}
reason1Selected = !reason1Selected;
reason1.setSelected(reason1Selected);
break;
case R.id.tv_reason2:
if(!reason2Selected){
MobclickAgent.onEvent(context,"zixun_toutiao_hate_detail", "zixun_toutiao_hate_detail_ad");
}
reason2Selected = !reason2Selected;
reason2.setSelected(reason2Selected);
break;
case R.id.tv_reason3:
if(!reason3Selected){
MobclickAgent.onEvent(context,"zixun_toutiao_hate_detail", "zixun_toutiao_hate_detail_source");
}
reason3Selected = !reason3Selected;
reason3.setSelected(reason3Selected);
break;
case R.id.tv_reason4:
if(!reason4Selected){
MobclickAgent.onEvent(context,"zixun_toutiao_hate_detail", "zixun_toutiao_hate_detail_tag");
}
reason4Selected = !reason4Selected;
reason4.setSelected(reason4Selected);
break;
case R.id.btn_commit:
if (listener != null) {
listener.onClick(getReason());
}
mPopupWindow.dismiss();
break;
}
if (reason1Selected || reason2Selected || reason3Selected || reason4Selected) {
btnCommit.setOnClickListener(this);
btnCommit.setText("确定");
} else {
btnCommit.setOnClickListener(null);
btnCommit.setText("不感兴趣");
}
}
public void setReasons(String reasons){
if(StringUtils.isNotEmpty(reasons)){
String[] split = reasons.split("_");
if(split.length>0){
if(split.length == 1){
String rea1 = split[0];
if(StringUtils.isNotEmpty(rea1)){
reason1.setText(rea1);
reason1.setVisibility(View.VISIBLE);
reason2.setVisibility(View.INVISIBLE);
reason3.setVisibility(View.GONE);
reason4.setVisibility(View.GONE);
reason_layout_2.setVisibility(View.GONE);
}
}else if(split.length == 2){
String rea1 = split[0];
if(StringUtils.isNotEmpty(rea1)){
reason1.setText(rea1);
reason1.setVisibility(View.VISIBLE);
}
String rea2 = split[1];
if(StringUtils.isNotEmpty(rea2)){
reason2.setText(rea2);
reason2.setVisibility(View.VISIBLE);
}
reason3.setVisibility(View.GONE);
reason4.setVisibility(View.GONE);
reason_layout_2.setVisibility(View.GONE);
}else if(split.length == 3){
String rea1 = split[0];
if(StringUtils.isNotEmpty(rea1)){
reason1.setText(rea1);
reason1.setVisibility(View.VISIBLE);
}
String rea2 = split[1];
if(StringUtils.isNotEmpty(rea2)){
reason2.setText(rea2);
reason2.setVisibility(View.VISIBLE);
}
String rea3 = split[2];
if(StringUtils.isNotEmpty(rea3)){
reason3.setText(rea3);
reason3.setVisibility(View.VISIBLE);
}
reason4.setVisibility(View.INVISIBLE);
reason_layout_2.setVisibility(View.VISIBLE);
}else if(split.length >= 3){
String rea1 = split[0];
if(StringUtils.isNotEmpty(rea1)){
reason1.setText(rea1);
reason1.setVisibility(View.VISIBLE);
}
String rea2 = split[1];
if(StringUtils.isNotEmpty(rea2)){
reason2.setText(rea2);
reason2.setVisibility(View.VISIBLE);
}
String rea3 = split[2];
if(StringUtils.isNotEmpty(rea3)){
reason3.setText(rea3);
reason3.setVisibility(View.VISIBLE);
}
String rea4 = split[3];
if(StringUtils.isNotEmpty(rea4)){
reason4.setText(rea4);
reason4.setVisibility(View.VISIBLE);
}
reason_layout_2.setVisibility(View.VISIBLE);
}
}
}else{
reason1.setText("重复、旧闻");
reason1.setVisibility(View.VISIBLE);
reason2.setText("广告");
reason2.setVisibility(View.VISIBLE);
reason3.setVisibility(View.GONE);
reason4.setVisibility(View.GONE);
reason_layout_2.setVisibility(View.GONE);
}
}
public String getReason() {
String content1 = reason1Selected ? reason1.getText().toString() + "_" : "";
String content2 = reason2Selected ? reason2.getText().toString() + "_" : "";
String content3 = reason3Selected ? reason3.getText().toString() + "_" : "";
String content4 = reason4Selected ? reason4.getText().toString() + "_" : "";
String s = content1 + content2 + content3 + content4;
return s.substring(0, s.length() - 1);
}
public interface OnCommitClickListener {
void onClick(String reason);
}
}
================================================
代码中如何调用:
FitPopupUtil fitPopupUtil = new FitPopupUtil((Activity) mContext,reasons);
fitPopupUtil.showPopup(anchorView);
fitPopupUtil.setOnClickListener(new FitPopupUtil.OnCommitClickListener() {
@Override
public void onClick(String reason) {
deleteNoInterestDate(position,reason);
Toast.makeText(mContext,reason,Toast.LENGTH_SHORT).show();
}
});