Skip to content

Instantly share code, notes, and snippets.

@slightfoot
Created November 17, 2015 18:33
Show Gist options
  • Save slightfoot/2a57cd2f400be38f8f5c to your computer and use it in GitHub Desktop.
Save slightfoot/2a57cd2f400be38f8f5c to your computer and use it in GitHub Desktop.
Action Panel Helper - For those popup panels from Action Buttons
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.PixelFormat;
import android.os.Build;
import android.support.annotation.LayoutRes;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.FrameLayout;
/**
* Action Panel Helper
* <p>
* Created by Simon Lightfoot on 15/11/2015.
*/
public class ActionPanelHelper
{
private final Context mContext;
private final WindowManager mWindowManager;
private WindowManager.LayoutParams mLayoutParams = new WindowManager.LayoutParams();
private boolean mShowing = false;
private PopupFrame mPopupFrame;
private View mContentView;
private int mAnimationStyle = -1;
private int mSoftInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED;
private boolean mFocusable = true;
private OnDismissListener mOnDismissListener;
public ActionPanelHelper(Context context)
{
mContext = context;
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
}
public View setContentView(View view)
{
mContentView = view;
if(mPopupFrame == null){
mPopupFrame = new PopupFrame(mContext, null);
}
mPopupFrame.removeAllViews();
mPopupFrame.addView(mContentView);
return mContentView;
}
public View setContentView(@LayoutRes int layoutResId)
{
setContentView(LayoutInflater.from(mContext).inflate(layoutResId, null));
return mContentView;
}
public View getContentView()
{
return mContentView;
}
public void setSoftInputMode(int softInputMode)
{
mSoftInputMode = softInputMode;
}
public void setFocusable(boolean focusable)
{
mFocusable = focusable;
}
public void setOnDismissListener(OnDismissListener onDismissListener)
{
mOnDismissListener = onDismissListener;
}
public void show(View anchor, int horizontalMargin, int verticalMargin)
{
if(!mShowing){
int measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
mPopupFrame.measure(measureSpec, measureSpec);
int[] location = new int[2];
anchor.getLocationInWindow(location);
mLayoutParams.x = (location[0] + anchor.getWidth()) - (mPopupFrame.getMeasuredWidth() - horizontalMargin);
mLayoutParams.y = location[1] + verticalMargin;
mLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
mLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
mLayoutParams.gravity = Gravity.START | Gravity.TOP;
mLayoutParams.flags =
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN |
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
if(!mFocusable){
mLayoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
if(mSoftInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED){
mLayoutParams.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
}
}
mLayoutParams.softInputMode = mSoftInputMode;
mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION;
mLayoutParams.format = PixelFormat.TRANSLUCENT;
mLayoutParams.windowAnimations = mAnimationStyle;
mWindowManager.addView(mPopupFrame, mLayoutParams);
mShowing = true;
}
}
public boolean isShowing()
{
return mShowing;
}
public void dismiss()
{
if(mShowing){
mWindowManager.removeView(mPopupFrame);
mShowing = false;
if(mOnDismissListener != null){
mOnDismissListener.onDismiss();
}
}
}
private class PopupFrame extends FrameLayout
{
@SuppressWarnings("deprecation")
public PopupFrame(Context context, AttributeSet attrs)
{
super(context, attrs);
int[] ATTRS = {
android.R.attr.popupBackground,
android.R.attr.popupAnimationStyle,
};
TypedArray a = context.obtainStyledAttributes(attrs, ATTRS,
R.attr.listPopupWindowStyle, 0);
if(a.hasValue(0)){
setBackgroundDrawable(a.getDrawable(0));
}
if(a.hasValue(1)){
mAnimationStyle = a.getResourceId(1, 0);
}
a.recycle();
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
int[] ATTRS_L = { android.R.attr.popupElevation };
a = context.obtainStyledAttributes(attrs, ATTRS_L,
R.attr.listPopupWindowStyle, 0);
if(a.hasValue(0)){
setElevation(a.getDimension(0, 0));
}
a.recycle();
}
}
@Override
public boolean onTouchEvent(MotionEvent event)
{
final int x = (int) event.getX();
final int y = (int) event.getY();
if((event.getAction() == MotionEvent.ACTION_DOWN) &&
((x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight())))
{
dismiss();
return true;
}
else if(event.getAction() == MotionEvent.ACTION_OUTSIDE){
dismiss();
return true;
}
else{
return super.onTouchEvent(event);
}
}
@Override
public boolean dispatchKeyEvent(KeyEvent event)
{
if(event.getKeyCode() == KeyEvent.KEYCODE_BACK){
final KeyEvent.DispatcherState state = getKeyDispatcherState();
if(state == null){
return super.dispatchKeyEvent(event);
}
if(event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0){
state.startTracking(event, this);
return true;
}
else if(event.getAction() == KeyEvent.ACTION_UP){
if(state.isTracking(event) && !event.isCanceled()){
dismiss();
return true;
}
}
return super.dispatchKeyEvent(event);
}
else{
return super.dispatchKeyEvent(event);
}
}
}
public interface OnDismissListener {
void onDismiss();
}
}
@slightfoot
Copy link
Author

Example of its use.

/*
<style name="Widget.App.PopupWindow" parent="ThemeOverlay.AppCompat.Light" />
*/
public class SomeActivity extends AppCompatActivity
{
    private ActionPanelHelper mActionPanelHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.something);
        createPopupWindow();
    }

    private void createPopupWindow()
    {
        Context context = new ContextThemeWrapper(this, R.style.Widget_App_PopupWindow);
        mActionPanelHelper = new ActionPanelHelper(context);

        View viewGroup = mActionPanelHelper.setContentView(R.layout.panel_text_size);
        SeekBar seekBar = (SeekBar) viewGroup.findViewById(R.id.text_size);
        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener()
        {
            @Override
            public void onStartTrackingTouch(SeekBar seekBar)
            {
                mContentView.startTextSizeTracking();
            }

            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
            {
                mContentView.setTextSize(progress);
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar)
            {
                mContentView.stopTextSizeTracking();
            }
        });
    }

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

    @Override
    public boolean onPrepareOptionsMenu(Menu menu)
    {
        MenuItem textSize = menu.findItem(R.id.action_text_size);
        final View textSizeView = MenuItemCompat.getActionView(textSize);
        textSizeView.setOnClickListener((v) -> {
            mActionPanelHelper.show(v, 8, 8);
        });
        return super.onPrepareOptionsMenu(menu);
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment