响应选择器的自定义可检查视图

Posted

技术标签:

【中文标题】响应选择器的自定义可检查视图【英文标题】:Custom Checkable View which responds to Selector 【发布时间】:2012-07-02 12:08:11 【问题描述】:

我有一组FrameLayout,我希望它是可检查/可选择的,

也就是说,单击后我希望FrameLayout 显示为checked - 再次按下时我希望它变为unchecked。更重要的是,我希望通过使用<selector> 来像往常一样描述这个视觉问题。

我似乎无法正常工作 - 我不确定我错过了什么:

public class CheckableFrameLayout extends FrameLayout implements Checkable 
    private boolean mChecked = false;
    public CheckableFrameLayout(Context context, AttributeSet attrs) 
        super(context, attrs);
    

    @Override
    public void setChecked(boolean checked) 
        mChecked = checked;
        refreshDrawableState();
    

    public boolean isChecked() 
        return mChecked;
    

    public void toggle() 
        setChecked(!mChecked);
    

CheckableFrameLayout的布局:

<com.test.view.CheckableFrameLayout
    android:layout_
    android:layout_
    android:background="@drawable/selector_horizontal"
    android:clickable="true" >

支持它的选择器(selector_horizo​​ntal.xml):

<item android:drawable="@drawable/selector_vertical_selected" android:state_pressed="false" android:state_checked="true"/>  
<item android:drawable="@drawable/selector_vertical_pressed" android:state_pressed="true" android:state_checked="false"/>
<item android:drawable="@drawable/selector_vertical_normal" android:state_pressed="false" android:state_checked="false"/>

使用上面的代码,“state_pressed”工作正常,但视图本身没有被检查(不是通过调试发现调用Checkable 代码)。

【问题讨论】:

【参考方案1】:

将以下代码添加到Checkable 类允许选择器工作:

private static final int[] CheckedStateSet = 
    android.R.attr.state_checked,
;

@Override
protected int[] onCreateDrawableState(int extraSpace) 
    final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
    if (isChecked()) 
        mergeDrawableStates(drawableState, CheckedStateSet);
    
    return drawableState;


@Override
public boolean performClick() 
    toggle();
    return super.performClick();

【讨论】:

onCreateDrawableState 由于某种原因没有从 LinearLayout 调用。任何想法为什么? 如果有人想获得完整的解决方案,请查看此存储库:github.com/shamanland/AndroidLayoutSelector 有自定义可点击/可检查 LinearLayout 就像 ToggleButton 这不支持初始检查状态android:checked="true"【参考方案2】:

这是 CheckableButton 的完整工作示例。它也适用于 Android 4.2。

public class CheckableButton extends Button implements Checkable 

    private static final int[] CheckedStateSet =  android.R.attr.state_checked ;

    private boolean mChecked = false;

    public CheckableButton(Context context) 
        super(context);
    

    public CheckableButton(Context context, AttributeSet attrs) 
        super(context, attrs);
    

    public CheckableButton(Context context, AttributeSet attrs, int defStyle) 
        super(context, attrs, defStyle);
    

    @Override
    public boolean isChecked() 
        return mChecked;
    

    @Override
    public void setChecked(boolean checked) 
        mChecked = checked;
        refreshDrawableState();
    

    @Override
    public void toggle() 
        mChecked = !mChecked;
        refreshDrawableState();
    

    @Override
    protected int[] onCreateDrawableState(int extraSpace) 
        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
        if (isChecked()) 
            mergeDrawableStates(drawableState, CheckedStateSet);
        
        return drawableState;
    

【讨论】:

你应该添加performClick()方法,看看接受的答案【参考方案3】:

尝试使用android:state_activated

<item android:drawable="@color/HighlightColor"      android:state_activated="true"/> 

来自docs:

StateListDrawable 的状态值,当视图或其父视图具有 已“激活”意味着用户当前已将其标记为 兴趣。

【讨论】:

我应该把那个标签放在哪里?不适用于&lt;item&gt; 它就像任何其他州一样。我在答案中添加了一个示例。 state_activated 完全没有区别(它也是 API 11) 我认为我们错过了明显的...android:checkable="true"。在布局中尝试。再说一次,也许不是,我看到提到的唯一地方是用于构建菜单......虽然我想无论如何尝试它不会有什么坏处。 嗯,在哪里?你有android:clickable,而不是android:checkable【参考方案4】:

尝试在您的selector_horizontal.xml 中重新排序标签这些是从上到下评估的。在您的情况下,似乎应用了 android:state_pressed="false" or android:state_pressed="true" 并且评估永远不会到达第三行 android:state_checked="true" 尝试将第一行移到最后:

<item android:drawable="@color/HighlightColor" android:state_pressed="true"/>
<item android:drawable="@color/selected_color" android:state_checked="true"/>
<item android:drawable="@android:color/transparent" android:state_pressed="false"/>

【讨论】:

没有区别 - 我确保每个项目都是隐式的(同时包含 state_pressed 和 state_checked),以免混淆。【参考方案5】:

除了上面格雷姆的回答 对toggle()做如下修改

@Override
public void toggle() 
    mChecked = !mChecked;
    refreshDrawableState();

【讨论】:

我已将其包含在原始答案中 这里唯一的补充是我们还需要在toggle()中调用refreshDrawableState()。【参考方案6】:

Graeme 的解决方案在 Android 4.0.3 下对我不起作用(我想它在 4.0 下也不起作用)。相反,您可以将state_checked 更改为state_activated

<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:drawable="@drawable/activated_image" android:state_pressed="false" android:state_activated="true" />
    <item android:drawable="@drawable/not_activated_image" android:state_pressed="false" android:state_activated="false"/>

</selector>

并在您的setChecked 中使用setActivated

@Override
public void setChecked(boolean checked) 
    mChecked = checked;
    setActivated(checked);

仅此而已。这里不需要实现onCreateDrawableState

【讨论】:

查看docs - “这是 state_checked 的另一种表示形式,用于将状态向下传播到视图层次结构。”,只有当您还想要您的孩子的孩子时,这样的声音才有用如果他们的父母被选中,查看回复true Graeme 的解决方案适用于我的可检查按钮(我还按照 sujith 的建议将 refreshDrawableState() 添加到 setChecked() 和 toggle()。

以上是关于响应选择器的自定义可检查视图的主要内容,如果未能解决你的问题,请参考以下文章

我们可以用angular中的自定义css覆盖选择器的默认样式吗

我的自定义 UIView 没有显示在屏幕上

如何为数组中的每个创建一个具有唯一图像和选择器的自定义 UIButton?

带有选择器的 SwiftUI 列表:选择不适用于自定义类型(macOS)

子类 UIControl 来制作自定义选择器

为啥由 UITabBarController.viewDidLoad 中的代码添加的自定义按钮不响应选择器