当状态为按下时如何移动按钮文本

17
我为一个按钮制作了自定义背景,并为不同的按钮状态也做了相应的背景。但现在我遇到了一个问题,无法理解。
当按钮处于正常状态时,它看起来很好。但是当我按下按钮时,我需要将文本向下移动几个像素,因为按钮背景图像在移动(实际上感觉像是在图像上移动,因为按钮下方有边框,当处于按下状态时,此边框消失)。请参见下面的图片。
当按钮状态为按下时,如何移动按钮中的文本?(也许可以通过某种填充或自定义按钮布局实现)

enter image description here


你可以尝试这个3D按钮的方法:http://stackoverflow.com/a/19248175/2149195 - RBK
赏金的补充说明:我现在已经尝试了很多关于9宫格图案的东西,以使文本内容移动。其中最有希望的是android:variablePadding="true",但我仍然没有成功(是的,我尝试在触摸事件上使视图无效...)。 - Sam
5个回答

6

我希望能够在9-patch中设置填充,但无法正常工作。在触摸监听器中设置填充很麻烦,在任何使用按钮的地方都会产生代码垃圾。

我选择对Button进行子类化,结果还算整洁。在我的情况下,我想将图标 (drawableLeft) 和文本向左和向下偏移1像素。

子类化按钮小部件:

package com.myapp.widgets;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.Button;

import com.myapp.R;

public class OffsetButton extends Button {

    public OffsetButton(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public OffsetButton(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public OffsetButton(Context context) {
        super(context);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        boolean value = super.onTouchEvent(event);

        if (event.getAction() == MotionEvent.ACTION_UP) {
            setBackgroundResource(R.drawable.btn_normal);
        } else if (event.getAction() == MotionEvent.ACTION_DOWN) {
            setBackgroundResource(R.drawable.btn_pressed);
            setPadding(getPaddingLeft() + 1, getPaddingTop() + 1, getPaddingRight() - 1,
                    getPaddingBottom() - 1);
        }

        return value;
    }
}

然后像这样在布局中使用它:

<com.myapp.widgets.OffsetButton
    android:text="@string/click_me"
    android:drawableLeft="@drawable/icon_will_be_offset_too_thats_good" />

注意事项:

  • 我没有使用 StateListDrawable 来设置背景,而是在代码中切换背景。当我尝试使用StateListDrawable时,会在 padding 改变和背景改变之间出现小的暂停,看起来不好看。

  • 设置背景会重置 padding,因此在 ACTION_UP 情况下不需要调整 padding。

  • 增加顶部和左侧 padding 同时减少底部和右侧 padding 很重要。这样内容区域的大小保持不变,实际上只是将内容区域移动了。


确实是一个非常整洁的解决方案。我会试一下。谢谢你的回答。 - evilone

4
我没有亲自尝试过,但如果您将九宫格作为两个状态的背景可绘制对象使用,则应考虑在按下状态的可绘制对象中设置适当的填充框。有关详细信息,请参见此处

我需要为按下的9-patch设置更多的顶部填充吗? - evilone
我会说是的。底部填充框部分应该向右移动。 - slkorolev
我认为这是最佳方法,用代码实现会很痛苦。 - evilone
这似乎对我不起作用。我正在调整内容区域,使其与按钮区域移动相同的距离,但文本从未移动,按钮只是被拉伸得更大,以便文本同时满足按下和未按下状态。 - Sam

3
你可以在视图上使用padding。你可以像这样为按钮或视图添加OnTouchListener:
viewF.setOnTouchListener(new View.OnTouchListener() {  
    @Override 
    public boolean onTouch(View v, MotionEvent event) { 
        if (event.getAction == MotionEvent.ACTION_UP) { 
            //set your padding            
        } else if (event.getAction == MotionEvent.ACTION_DOWN){
            //set your padding            
        }   
        return true; 
    } 
}); 

ontouchlistener会在按钮被按下和释放时通知您。


正如我在之前的回答中所说,不管是ontouchlistener还是gesture,都无法给你按钮的“按下”状态。如果按钮被滚动,你将得到在ACTION_DOWN中设置的填充值。你甚至不能再得到ACTION_UP事件来将填充值更改为默认值。拥有24k声望却回答如此,真是可惜。我没有点踩是因为我心情很好。 - γηράσκω δ' αεί πολλά διδασκόμε

1

彼得里斯·考恩的回答比覆盖setPressed()方法效果更差。但是,只要在ICS或更低版本的设备上测试您的应用程序并将按钮添加为listview项,setPressed就可以正常工作。 为了实现这一点,我改进了我的按钮类:

public class OffsetButton extends Button {

    private static final int OFFSET_IN_DP = 6;
    private int offset_in_px;
    private boolean wasPressed = false;
    private Integer[] defaultPaddings;

    public OffsetButton(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initView();
    }

    public OffsetButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public OffsetButton(Context context) {
        super(context);
        initView();
    }

    private void initView() {
        offset_in_px = (int) DisplayUtil.convertDpToPixel(OFFSET_IN_DP);
    }

    @Override
    public void setPressed(boolean pressed) {
        if (pressed && !wasPressed) {
            changePaddings();
        }
        if (!pressed && wasPressed) {
            resetPaddings();
        }
        super.setPressed(pressed);
    }

    private void changePaddings() {
        defaultPaddings = new Integer[]{getPaddingLeft(), getPaddingTop(), getPaddingRight(), getPaddingBottom()};
        setPadding(getPaddingLeft(), getPaddingTop() + offset_in_px, getPaddingRight(), getPaddingBottom() - offset_in_px);
        wasPressed = true;
    }

    private void resetPaddings() {
        setPadding(defaultPaddings[0], defaultPaddings[1], defaultPaddings[2], defaultPaddings[3]);
        wasPressed = false;
    }

    @Override
    public boolean performClick() {
        resetPaddings();
        return super.performClick();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (isEnabled())
        {
            if (event.getAction() == MotionEvent.ACTION_DOWN && !wasPressed) {
                changePaddings();
            } else if (event.getAction() == MotionEvent.ACTION_UP && wasPressed) {
                resetPaddings();
            }
        }
        return super.onTouchEvent(event);
    }
}

当我在linearlayout中放置两个并排的按钮时,我遇到了问题。尽管添加和移除了相同数量的padding,但在按下按钮时,按钮的高度会改变,导致linearlayout的高度也发生变化。 - Sam
我通过将LinearLayout的baselineAligned设置为false来解决了上述问题。在onTouchEvent中检查是否启用可能是值得的。 - Sam

0

这可能会起作用:

setPadding(left, top, right, bottom); // Normal
setPadding(left, top + x, right, bottom - x); // Pressed

@LASVEGAS 我如何检测按钮是否处于按下状态? - evilone
我猜你需要添加View.OnTouchListener并监听MotionEvent.ACTION_DOWN。 - Caner
...& 监听 MotionEvent.ACTION_DOWN。这并不像你说的那么简单。对于一个静态按钮来说还好,但当按钮被滚动时会发生什么呢?ACTION_DOWN 与 xml 选择器中的 pressed state = true 不同。没有一个事件可以捕获 pressed state 并执行相应操作。 - γηράσκω δ' αεί πολλά διδασκόμε

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接