-->

How to switch button image from on to off and vice

2019-04-10 20:32发布

问题:

I have been trying to switch the state of my buttons from on to off and vice versa, by adding an image for on and adding an image for off state. I tried through xml, however I was just able to temporarily switch it upon click (by using press/focus etc). Here is the code for the same:

fragment_justin:

<RelativeLayout
        android:id="@+id/top_bar_container"
        android:layout_width="match_parent"
        android:layout_height="48.5dp"
        android:layout_alignParentTop="true"
        android:background="@color/background_action_bar"
        android:orientation="horizontal" >

        <ImageButton
            android:id="@+id/menu_button"
            android:layout_width="30dp"
            android:layout_height="48.5dp"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:background="@android:color/transparent"
            android:paddingRight="24dp"
            android:src="@drawable/overflow_btn" />

        <com.justin.abc.FontTextView
            android:id="@+id/most_recent_text"
            android:layout_width="wrap_content"
            android:layout_height="48.5dp"
            android:layout_marginLeft="11dp"
            android:layout_marginRight="2dp"
            android:textColor="@color/white"
            foo:customFont="Cabin-Bold.ttf"
            android:layout_alignParentLeft="true"
            android:textSize="18sp"
            android:gravity="center_vertical"
            android:text="@string/most_recent"/>

    </RelativeLayout>

Focussing just on the imagebutton part,you'll see the overflow_btn. Now overflow_btn is a class called under drawable , as follows:

overflow_btn:

 <?xml version="1.0" encoding="utf-8"?>

<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
    android:state_enabled="false"
    android:drawable="@drawable/overflow_pressed" />
<item
    android:state_pressed="true"
    android:state_enabled="true"
    android:drawable="@drawable/overflow_pressed" />
<item
    android:state_focused="true"
    android:state_enabled="true"
    android:drawable="@drawable/overflow_pressed" />
<item
    android:state_enabled="true"
    android:drawable="@drawable/overflow" />
</selector>

Upon using the above code, it temporarily switches the button states only when tapped ( like it flashes to overflow_pressed) and reverts back to overflow. I believe there might be some changes required in the java code for this to happen,Hence here's the corresponding java class :

@Override
    public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
        final View view = inflater.inflate(R.layout.fragment_justin, container, false);
        createMenus() ;
        setOnclickListeners(view);

        mAdapter = new CursorAdapter(getActivity(), null, 0);
        final ListView list = (ListView) view.findViewById(R.id.justin_list);
        list.setAdapter(mAdapter);
        list.setOnItemLongClickListener(this);
        list.setOnTouchListener(this);
        list.setOnItemClickListener(this);

        LayoutUtils.showLoading(view, R.id.justin_list);
        return view;
    }

    private void setOnclickListeners(final View view){
        final ImageButton button = (ImageButton) view.findViewById(R.id.menu_button);
        button.setOnClickListener(this);
    }

    private void createMenus() {
        mPreferenceMenuItems.add(PreferenceMenuItems.JUSTIN_PREFERENCES);
        mPreferencesMenuHelper = new MenuHelper(getActivity(), mPreferenceMenuItems,this);

        mDeleteMenuItems.add(DeleteMenuItems.DELETE_JUSTIN);
        mDeleteMenuHelper = new MenuHelper(getActivity(), mDeleteMenuItems,this);
    }

    @Override
    public void onResume() {
        super.onResume();
        final MainActivity activity = (MainActivity) getActivity();
        activity.updateActionBarTitle();
        final IntentFilter filter = new IntentFilter();
        activity.getApplicationContext().registerReceiver(mReceiver, filter);
    }

    @Override
    public void onPause() {
        getActivity().getApplicationContext().unregisterReceiver(mReceiver);
        super.onPause();
    }

    @Override
    public void onCursorLoaded(final Uri uri, final Cursor cursor) {
        if (cursor.getCount() > 0) {
            mAdapter.swapCursor(cursor);
            showResults(uri);
        } else {
            if (!isOperationExecuting()) {
                showNoResults(uri);
            }
        }
    }


    @Override
    public void onMenuItemClicked(final int position) {
        if (isPreferences) {
            switch (position) {
                case PreferenceMenuItems.JUSTIN_PREFERENCES_POSITION:
                    PreferencesActivity.newInstance(getActivity());
                    break;
                default:
                    break;
            }
            mPreferencesMenuHelper.hideMenu();
        } else {
            switch (position) {
                case DeleteMenuItems.DELETE_POSITION:
                    final DialogFragment dialog = new DeleteFromDialogFragment();
                    final Bundle bundle = new Bundle();
                    bundle.putString(JUSTIN_ID, mJustinID);
                    dialog.setArguments(bundle);
                    dialog.show(getFragmentManager(), DELETE_FROM_JUSTIN );
                    break;
                default:
                    break;
            }
            mDeleteMenuHelper.hideMenu();
        }
    }

    @Override
    public void onClick(final View view) {
        switch (view.getId()) {
            case R.id.justin_menu_button:
                final Activity activity = getActivity();
                final int actionBarHeight = activity.findViewById(R.id.title_main_container).getHeight();
                final int menuBarHeight = activity.findViewById(R.id.justin_top_bar_container).getHeight();
                mPreferencesMenuHelper.showMenu(actionBarHeight + menuBarHeight, Gravity.RIGHT, R.style.MenuDialogAnimation);
                isPreferences = true;

                break;
            default:
                break;
        }
    }

    @Override
    public void showResults(final Uri uri) {
        if (mIsAnimating) {
            mShouldShowResult = true;
        } else {
            LayoutUtils.showResults(getView(), R.id.justin_list);
            mShouldShowResult = false;
        }
    }

    @Override
    public void showNoResults(final Uri uri) {
        if (mIsAnimating) {
            mShouldShowNoResult  = true;
        } else {
            LayoutUtils.showNoResult(getView(), R.id.justin_list, getResources().getString(R.string.no_result));
            mShouldShowNoResult = false;
        }
    }

    @Override
    public void onOperationStarted(final Uri uri) {
        LayoutUtils.showLoading(getView(), R.id.justin_list);
    }

    @Override
    public boolean onItemLongClick(final AdapterView<?> adapter, final View view, final int position, final long id) {
        isPreferences = false;
        final Activity activity = getActivity();
        final int actionBarHeight = activity.findViewById(R.id.title_main_container).getHeight();
        final int menuBarHeight = activity.findViewById(R.id.justin_top_bar_container).getHeight();
        mDeleteMenuHelper.showMenu(mYValue + actionBarHeight + menuBarHeight, Gravity.CENTER, R.style.MenuDialogAnimation);

        final Cursor cursor = (Cursor) mAdapter.getItem(position);
        mJustinID = cursor.getString(cursor.getColumnIndex(Justin.Columns.ID));

        return false;
    }

    @Override
    public boolean onTouch(final View v, final MotionEvent event) {
        if(event.getAction() == MotionEvent.ACTION_DOWN){
            mYValue = (int)event.getY();
        }

        return false;
    }

    public void restartLoader(){
        getContentLoader().restartLoader();
    }

    @Override
    public void onItemClick(final AdapterView<?> adapter, final View view, final int position, final long id) {
        final Cursor cursor = (Cursor) mAdapter.getItem(position);
        if (!NetworkUtils.isOnline() && ContentProvider.isStoryEmty(cursor)) {
            final DialogFragment dialog = new CheckOfflineAccessDialogFragment();
            dialog.show(getFragmentManager(), OFFLINE_ACCESS);
            return;
        }

        final String documentSource = cursor.getString(cursor.getColumnIndex(Justin.Columns.SOURCE));
        final ArticlePagerFragment ArticlePagerFragment = ArticlePagerFragment.newInstance(getFragmentManager(), position, documentSource, false);
        ArticlePagerFragment.setFragment(this);

    }

    public static String getArticleID(final Cursor cursor) {
        final String documentLink = cursor.getString(cursor.getColumnIndex(Justin.Columns.DOCUMENT_LINK));
        final String documentType = cursor.getString(cursor.getColumnIndex(Justin.Columns.TYPE));
        final String source = cursor.getString(cursor.getColumnIndex(Justin.Columns.SOURCE));
        String articleID = "";
        if (source.equalsIgnoreCase(Justin.NEWS_SOURCE_VALUE) && documentType.equalsIgnoreCase(Justin.TEXT_TYPE)) {
            articleID = documentLink.substring(documentLink.lastIndexOf("storyId=")+8);
        }
        return articleID;
    }

Any idea how to go about the same?

MenuHelper:

public class MenuHelper implements OnItemClickListener{

    private final Dialog mMenu;
    private final OnMenuItemClickedListener mMenuItemListener;
    private final ArrayAdapter<String> mAdapter;
    private final int MENU_ITEM_HEIGHT = 40; // Defined in res/layout/menu_item.xml

    private List<Boolean> enabledStates;

    public MenuHelper(final Context context, final List<String> menuItems, final OnMenuItemClickedListener menuItemListener) {
        mMenu = new Dialog(context);
        mMenu.requestWindowFeature(Window.FEATURE_NO_TITLE);
        mMenu.setContentView(R.layout.menu_container);
        enabledStates = new ArrayList<Boolean>(menuItems.size());
        for(int i = 0; i < menuItems.size(); i++) {
            enabledStates.add(true);
        }

        final ListView menuList = (ListView) mMenu.findViewById(R.id.menu_list);
        mAdapter = new ArrayAdapter<String>(context, R.layout.menu_item, menuItems) {
            @Override
            public boolean isEnabled(int position) {
                return enabledStates.get(position);
            }

            @Override
            public boolean areAllItemsEnabled() {
                return false;
            }

            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
                View view = super.getView(position, convertView, parent);
                if(enabledStates.get(position)) {
                    ((TextView) view).setTextColor(Application.getAppResources().getColor(R.color.white));
                } else {
                    ((TextView) view).setTextColor(Application.getAppResources().getColor(R.color.disabled_gray));
                }
                if (convertView == null) {
                    convertView = super.getView(position, convertView, parent);
                }

                // check for odd or even to set alternate colors to the row background
                if (position % 2 == 0) {
                    convertView.setBackgroundResource(R.drawable.oddcellcolor);
                } else {
                    convertView.setBackgroundResource(R.drawable.evencellcolor);
                }
                return convertView;
                //return view;
            }
        };
        menuList.setOnItemClickListener(this);
        menuList.setAdapter(mAdapter);
        mMenuItemListener = menuItemListener;
    }

    public void showMenu(final int yPosition, final int horizontalGravity) {
        showMenu(yPosition, horizontalGravity, R.style.MenuDialogAnimation);
    }

    public void setEnabled(int position, boolean isEnabled) {
        enabledStates.set(position, isEnabled);
        mAdapter.notifyDataSetChanged();
    }

    public void setEnabledAll(boolean isEnabled) {
        for(int ii = 0; ii < enabledStates.size(); ii++) {
            enabledStates.set(ii, isEnabled);
        }
        mAdapter.notifyDataSetChanged();
    }

    public int getMenuHeight() {
        if (mMenu != null) {
            return (int) (mAdapter.getCount() * MENU_ITEM_HEIGHT  / Application.getAppResources().getDisplayMetrics().density);
        } else {
            return -1;
        }
    }

    public void showMenu(final float yPosition, final int horizontalGravity, final int animation) {
        final Window window = mMenu.getWindow();
        window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
        if (horizontalGravity != Gravity.CENTER) {
            window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
        }
        final WindowManager.LayoutParams params = window.getAttributes();
        params.gravity = Gravity.TOP | horizontalGravity;
        params.y = Math.round(yPosition);
        params.windowAnimations = animation;
        window.setAttributes(params);
        mMenu.show();
    }

    public void hideMenu() {
        mMenu.hide();
    }

    @Override
    public void onItemClick(final AdapterView<?> adapter, final View view, final int position, final long parent) {
        mMenuItemListener.onMenuItemClicked(position);
    }


}

Thanks! Justin

回答1:

You can do this in code using:

// Declare button
// Set its original background image
button.setBackground(getResources().getDrawable(R.drawable.original_button_image));

button.setOnClickListener(new OnClickListener() {

    @Override
    public void onClick(View v) {

        // Choose whichever image for pressed state
        v.setBackground(getResources().getDrawable(R.drawable.button_is_pressed)); 

        new Handler().postDelayed(new Runnable() {

            public void run() {

                v.setBackground(getResources().getDrawable(R.drawable.original_button_image));

                // Button Click Code Here
            }

        }, 100L);    // Change this value to whatever is suitable

    }

});

Edit 1:

<ImageButton
        android:id="@+id/menu_button"
        android:layout_width="30dp"
        android:layout_height="48.5dp"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:background="@android:color/transparent"
        android:src="@drawable/testdrawable"
        android:paddingRight="24dp" />

Since the button has only two states, try testdrawable instead of the one you posted in your question:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item 
        android:state_selected="true" 
        android:drawable="@drawable/overflow_pressed" />
    <item 
        android:drawable="@drawable/overflow" />
</selector>

Change your onClick(View) to:

@Override
public void onClick(final View view) {
    switch (view.getId()) {
        case R.id.justin_menu_button:

            if (button.isSelected()) {
                button.setSelected(false);
                isPreferences = false;
                mPreferencesMenuHelper.hideMenu();
                Log.i("ImageButtonCheck", "Button is not selected anymore");
            } else {
                button.setSelected(true);
                final Activity activity = getActivity();
                final int actionBarHeight = activity.findViewById(R.id.title_main_container).getHeight();
                final int menuBarHeight = activity.findViewById(R.id.justin_top_bar_container).getHeight();
                mPreferencesMenuHelper.showMenu(actionBarHeight + menuBarHeight, Gravity.RIGHT, R.style.MenuDialogAnimation);
                isPreferences = true;
                Log.i("ImageButtonCheck", "Button is in selected state");
            }

            break;
        default:
            break;
    }
}

Change onMenuItemClicked(int) to:

@Override
public void onMenuItemClicked(final int position) {
    if (isPreferences) {
        switch (position) {
            case PreferenceMenuItems.JUSTIN_PREFERENCES_POSITION:
                PreferencesActivity.newInstance(getActivity());
                break;
            default:
                break;
        }
        isPreferences = false;
        button.setSelected(false);
        mPreferencesMenuHelper.hideMenu();
        Log.i("ImageButtonCheck", "Button is not in a selected state because of a menu item click");
    } else {
        switch (position) {
            case DeleteMenuItems.DELETE_POSITION:
                final DialogFragment dialog = new DeleteFromDialogFragment();
                final Bundle bundle = new Bundle();
                bundle.putString(JUSTIN_ID, mJustinID);
                dialog.setArguments(bundle);
                dialog.show(getFragmentManager(), DELETE_FROM_JUSTIN );
                break;
            default:
                break;
        }
        mDeleteMenuHelper.hideMenu();
    }
}

I think the problem here is that when the button is first pressed, the selected state is achieved and the menu is shown. But, clicking the button again, while the menu is shown, does not call the onClick(View) method. I have included 3 log statements in the code above. Let me know of the output you see in logcat when you click the button once to show the menu and click the menu again to hide it. The output from these two clicks should be "Button is in selected state" and "Button is not selected anymore".

Edit 2:

Include MenuHelper as an inner class in your activity class:

public class MenuHelper implements OnItemClickListener, Dialog.OnDismissListener {

    private final Dialog mMenu;
    private final OnMenuItemClickedListener mMenuItemListener;
    private final ArrayAdapter<String> mAdapter;
    private final int MENU_ITEM_HEIGHT = 40; // Defined in res/layout/menu_item.xml

    private List<Boolean> enabledStates;

    public MenuHelper(final Context context, final List<String> menuItems, final OnMenuItemClickedListener menuItemListener) {
        mMenu = new Dialog(context);
        mMenu.requestWindowFeature(Window.FEATURE_NO_TITLE);
        mMenu.setContentView(R.layout.menu_container);
        mMenu.setOnDismissListener(this);  -------------------------> **add this**
        enabledStates = new ArrayList<Boolean>(menuItems.size());
        for(int i = 0; i < menuItems.size(); i++) {
            enabledStates.add(true);
        }

        final ListView menuList = (ListView) mMenu.findViewById(R.id.menu_list);
        mAdapter = new ArrayAdapter<String>(context, R.layout.menu_item, menuItems) {
            @Override
            public boolean isEnabled(int position) {
                return enabledStates.get(position);
            }

            @Override
            public boolean areAllItemsEnabled() {
                return false;
            }

            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
                View view = super.getView(position, convertView, parent);
                if(enabledStates.get(position)) {
                    ((TextView) view).setTextColor(Application.getAppResources().getColor(R.color.white));
                } else {
                    ((TextView) view).setTextColor(Application.getAppResources().getColor(R.color.disabled_gray));
                }
                if (convertView == null) {
                    convertView = super.getView(position, convertView, parent);
                }

                // check for odd or even to set alternate colors to the row background
                if (position % 2 == 0) {
                    convertView.setBackgroundResource(R.drawable.oddcellcolor);
                } else {
                    convertView.setBackgroundResource(R.drawable.evencellcolor);
                }
                return convertView;
                //return view;
            }
        };
        menuList.setOnItemClickListener(this);
        menuList.setAdapter(mAdapter);
        mMenuItemListener = menuItemListener;
    }

    public void showMenu(final int yPosition, final int horizontalGravity) {
        showMenu(yPosition, horizontalGravity, R.style.MenuDialogAnimation);
    }

    public void setEnabled(int position, boolean isEnabled) {
        enabledStates.set(position, isEnabled);
        mAdapter.notifyDataSetChanged();
    }

    public void setEnabledAll(boolean isEnabled) {
        for(int ii = 0; ii < enabledStates.size(); ii++) {
            enabledStates.set(ii, isEnabled);
        }
        mAdapter.notifyDataSetChanged();
    }

    public int getMenuHeight() {
        if (mMenu != null) {
            return (int) (mAdapter.getCount() * MENU_ITEM_HEIGHT  / Application.getAppResources().getDisplayMetrics().density);
        } else {
            return -1;
        }
    }

    public void showMenu(final float yPosition, final int horizontalGravity, final int animation) {
        final Window window = mMenu.getWindow();
        window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
        if (horizontalGravity != Gravity.CENTER) {
            window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
        }
        final WindowManager.LayoutParams params = window.getAttributes();
        params.gravity = Gravity.TOP | horizontalGravity;
        params.y = Math.round(yPosition);
        params.windowAnimations = animation;
        window.setAttributes(params);
        mMenu.show();
    }

    public void hideMenu() {
        mMenu.hide();
    }

    @Override
    public void onItemClick(final AdapterView<?> adapter, final View view, final int position, final long parent) {
        mMenuItemListener.onMenuItemClicked(position);
    }

    @Override
    public void onDismiss(DialogInterface arg0) {
        button.setSelected(false);
    }

}


回答2:

I agree with @Chronos, it sounds more like a ToggleButton with two states. Keep it simple, here's mine you can use as an example. No code necessary, just xml.

here's the xml for my layout:

<ToggleButton
        android:id="@+id/btnHeadache"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_centerHorizontal="true"
        android:background="@drawable/headache_button"
        android:paddingTop="25sp"
        android:text="@string/headache"
        android:textOn=""
        android:textOff="" />

Now here's the xml for the different state's, that is of course inside my drawable folder with the images. This is named headache_button.xml in case its not clear above.:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/check"  android:state_checked="true"/> <!-- currently pressed turning the toggle on -->
    <item android:drawable="@drawable/headache" android:state_checked="false" /> <!-- currently pressed turning the toggle off   -->
</selector>


回答3:

It sounds more like you should be using a CheckBox. Regular buttons don't have an "on" state, just pressed and focused. With a checkbox, you'll need to use android:state_checked="true". Clicking the checkbox will switch to the checked state, clicking it again switches it back to default (unchecked).

Try not to add extra java code until you're sure that you can't do something with XML.



回答4:

If I understand fine you need a ToggleButton, a button with two states.

And use a selector like this one:

<?xml version="1.0" encoding="UTF-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:drawable="@drawable/btn_normal" android:state_checked="false"/>
    <item android:drawable="@drawable/btn_pressed" android:state_checked="true"/>

</selector>


回答5:

If I've understood you right, you want a 'Button' that has two states and taping it toggles between them.

In which case, it looks to more like you want the behavior of a 'ToggleButton', 'CompoundButton' or 'Switch' . You can configure them in xml using 'android :state_checked' as per @Chronos.