安卓可选LinearLayout状态

4

我正在尝试实现一个可选中的线性布局,已经完成了选中项目,但现在onclick和onLongClick没有按照预期工作,有什么想法可以实现这种行为。

  • 如果未选中并且OnLongClick = 选中
  • 如果已选中并且onClick = 取消选中
  • 如果已选中并且onLongClick = 取消选中
  • 如果未选中并且onClick = super.performClick()

我的可选中LinearLayout

public class CheckableLinearLayout extends LinearLayout implements Checkable {

/**
 * Interface definition for a callback to be invoked when the checked state
 * of this View is changed.
 */
public static interface OnCheckedChangeListener {

    /**
     * Called when the checked state of a compound button has changed.
     * 
     * @param checkableView
     *            The view whose state has changed.
     * @param isChecked
     *            The new checked state of checkableView.
     */
    void onCheckedChanged(View checkableView, boolean isChecked);
}

private static final int[] CHECKED_STATE_SET = { android.R.attr.state_checked };

private boolean mChecked = false;

private OnCheckedChangeListener mOnCheckedChangeListener;

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

public boolean isChecked() {
    return mChecked;
}

public void setChecked(boolean b) {
    if (b != mChecked) {
        mChecked = b;
        refreshDrawableState();

        if (mOnCheckedChangeListener != null) {
            mOnCheckedChangeListener.onCheckedChanged(this, mChecked);
        }
    }
}

public void toggle() {
    setChecked(!mChecked);
}

@Override
public int[] onCreateDrawableState(int extraSpace) {
    final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
    if (isChecked()) {
        mergeDrawableStates(drawableState, CHECKED_STATE_SET);
    }
    return drawableState;
}

/**
 * Register a callback to be invoked when the checked state of this view
 * changes.
 * 
 * @param listener
 *            the callback to call on checked state change
 */
public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
    mOnCheckedChangeListener = listener;
}

@Override
public boolean performLongClick() {
    toggle();
    return super.performLongClick();
}

@Override
public boolean performClick() {

    if (isChecked()) {
        toggle();
        return false;
    } else
        return super.performClick();
}

我的状态可绘制 XML

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

<item android:drawable="@drawable/add_schedule_button_checked_pressed" android:state_checked="true" android:state_pressed="true"/>

<!-- <item android:drawable="@drawable/add_schedule_button_checked_focused" -->
<!-- android:state_checked="true" -->
<!-- android:state_focused="true" /> -->

<item android:drawable="@drawable/selected_card_shape_w_shadow_white" android:state_checked="true"/>

<!-- <item android:drawable="@drawable/add_schedule_button_unchecked_pressed" -->
<!-- android:state_pressed="true" /> -->


<!-- <item android:drawable="@drawable/add_schedule_button_unchecked_focused" -->
<!-- android:state_focused="true" /> -->

我的布局

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/topics_preview_main_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" >

<LinearLayout
    android:id="@+id/favorites_header_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginLeft="6dp"
    android:layout_marginRight="6dp"
    android:layout_marginTop="2dp"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/favorites_header_title"
        style="@style/CardText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="4dp"
        android:layout_marginLeft="6dp"
        android:layout_marginRight="6dp"
        android:layout_marginTop="4dp"
        android:background="@drawable/card"
        android:gravity="left"
        android:paddingBottom="10dp"
        android:paddingLeft="5dp"
        android:paddingTop="10dp"
        android:text="@string/favorites_header" />

    <ui.CheckableLinearLayout
        android:id="@+id/favorites_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="4dp"
        android:layout_marginLeft="6dp"
        android:layout_marginRight="6dp"
        android:layout_marginTop="4dp"
        android:background="@drawable/selector_card_shape_w_shadow_white"
        android:clickable="true"
        android:longClickable="true"
        android:orientation="vertical"
        android:paddingBottom="16dp" >

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_vertical"
            android:paddingLeft="8dp"
            android:paddingRight="8dp" >

            <TextView
                android:id="@+id/favorites_title"
                style="@style/CardTitle"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="title" />
        </LinearLayout>

        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:layout_marginTop="4dp"
            android:background="@color/C_Favorites_Pink" />

        <LinearLayout
            android:id="@+id/favorites_description_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_vertical"
            android:orientation="vertical"
            android:padding="4dp" >

            <TextView
                android:id="@+id/favorites_description"
                style="@style/CardText"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginLeft="8dp"
                android:ellipsize="end"
                android:maxLines="4"
                android:text="Lorem ipsum dolor sit amet" />
        </LinearLayout>
    </ui.CheckableLinearLayout>
</LinearLayout>

我的片段

private void onFavoriteClick(final MCourse courseInfo,
        LinearLayout favoritesLayout) {
    favoritesLayout.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            String courseId = Integer.toString(v.getId());
            String courseName = courseInfo.getFullname();

            Bundle bundle = new Bundle();
            bundle.putString("courseId", courseId);
            bundle.putString("courseName", courseName);

            FragmentManager fragmentManager = getFragmentManager();
            FragmentTransaction fragmentTransaction = fragmentManager
                    .beginTransaction();

            TopicsPreview insideTopicsFrag = new TopicsPreview();
            insideTopicsFrag.setArguments(bundle);
            fragmentTransaction
                    .replace(R.id.mainFragment, insideTopicsFrag);
            fragmentTransaction.commit();

        }
    });
}

1
如果有人想获取完整的解决方案,请查看此存储库:https://github.com/shamanland/AndroidLayoutSelector,其中包含自定义可点击/可选中的LinearLayout,就像ToggleButton一样。 - Oleksii K.
使用这个库,选中的可绘制物会覆盖布局的全尺寸!? 在这里检查问题:(http://stackoverflow.com/questions/19099467/android-selector-draw-doesnt-overlap) - firetrap
1个回答

5
为了修复您当前的代码,我在此发布了一个完整的代码解决方案(因为我不理解您的代码哪里出了问题)。它非常相似,只是没有您想要的额外逻辑。
或者,您可以使用下一个解决方案:
您可以在 CTOR 上调用 setOnClickListener 和 setOnLongClickListener,并根据当前状态在新函数中执行逻辑,如下所示:
public CheckableLinearLayout(Context context, AttributeSet attrs) 
  {
  super(context, attrs);
  setOnClickListener(null);
  }

为了让“外部世界”与这些新方法相协作,您需要将它们制作成包装器,例如:
public void setOnClickListener(... listener)
  {
  super.setOnClickListener(new ...
    {
    @Override
    public void onClick(...)
      {
      // TODO add add logic that you wanted according to the state of the view
      if(listener!=null) 
        listener.onClick(...);
      }
    });
  }

长按操作应该实现类似的功能。

顺便提一下,您忘记添加对savedState bundle的处理了。请阅读此处以获取缺失的内容。

以下是我已测试过与布局“可检查性”兼容的代码:

public class CheckableLinearLayout extends LinearLayout implements Checkable
  {
  private boolean             mChecked;
  private static final String TAG               =CheckableLinearLayout.class.getCanonicalName();
  private static final int[]  CHECKED_STATE_SET = {android.R.attr.state_checked};

  public CheckableLinearLayout(final Context context)
    {
    super(context);
    setClickable(true);
    setLongClickable(true);
    }

  public CheckableLinearLayout(final Context context,final AttributeSet attrs)
    {
    super(context,attrs);
    setClickable(true);
    setLongClickable(true);
    }

  @TargetApi(Build.VERSION_CODES.HONEYCOMB)
  public CheckableLinearLayout(final Context context,final AttributeSet attrs,final int defStyle)
    {
    super(context,attrs,defStyle);
    setClickable(true);
    setLongClickable(true);
    }

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

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

  @Override
  protected void drawableStateChanged()
    {
    super.drawableStateChanged();
    final Drawable drawable=getBackground();
    if(drawable!=null)
      {
      final int[] myDrawableState=getDrawableState();
      drawable.setState(myDrawableState);
      invalidate();
      }
    }

  @Override
  public boolean performClick()
    {
    Toast.makeText(getContext(),"click",Toast.LENGTH_SHORT).show();
    return super.performClick();
    }

  @Override
  public boolean performLongClick()
    {
    Toast.makeText(getContext(),"long click",Toast.LENGTH_SHORT).show();
    return super.performLongClick();
    }

  @Override
  public boolean isChecked()
    {
    return mChecked;
    }

  @Override
  public void toggle()
    {
    setChecked(!mChecked);
    }

  @Override
  public Parcelable onSaveInstanceState()
    {
    // Force our ancestor class to save its state
    final Parcelable superState=super.onSaveInstanceState();
    final SavedState savedState=new SavedState(superState);
    savedState.checked=isChecked();
    return savedState;
    }

  @Override
  public void onRestoreInstanceState(final Parcelable state)
    {
    final SavedState savedState=(SavedState)state;
    super.onRestoreInstanceState(savedState.getSuperState());
    setChecked(savedState.checked);
    requestLayout();
    }

  // /////////////
  // SavedState //
  // /////////////
  private static class SavedState extends BaseSavedState
    {
    boolean                                            checked;
    @SuppressWarnings("unused")
    public static final Parcelable.Creator<SavedState> CREATOR;
    static
      {
      CREATOR=new Parcelable.Creator<SavedState>()
        {
          @Override
          public SavedState createFromParcel(final Parcel in)
            {
            return new SavedState(in);
            }

          @Override
          public SavedState[] newArray(final int size)
            {
            return new SavedState[size];
            }
        };
      }

    SavedState(final Parcelable superState)
      {
      super(superState);
      }

    private SavedState(final Parcel in)
      {
      super(in);
      checked=(Boolean)in.readValue(null);
      }

    @Override
    public void writeToParcel(final Parcel out,final int flags)
      {
      super.writeToParcel(out,flags);
      out.writeValue(checked);
      }

    @Override
    public String toString()
      {
      return TAG+".SavedState{"+Integer.toHexString(System.identityHashCode(this))+" checked="+checked+"}";
      }
    }
  }

让我看看我是否理解你的意思,我需要将setOnClick的监听器放在这个视图的构造函数中吗?我正在实现onclickListener在充气此布局的片段中。你能否更好地解释一下你的意思。谢谢;) - firetrap
我不理解。不管怎样,我已经更新了我的答案,并加入了一个完整可用的代码。请注意,如果您希望处理触摸事件,即使布局中有可点击的子元素,您必须重写onInterceptTouchEvent并返回true。 - android developer
我已经实现了你给我的“缺失部分”,并加入了我的额外功能,一切都开始工作了。我会在更新中发布我的代码以帮助其他人。我甚至没有尝试过你的CTOR解决方案和包装器,只是因为对我来说,第一种方法似乎更干净整洁。但现在我有一个设计问题,因为只有子元素是可选的,所以绘图将不能填充未选中的绘图。图片 - firetrap
不,我认为不是那个问题,可能是图像重叠的某些原因;选择器中的第二个绘制(即灰色的那个)无法完全覆盖原始绘制。你看到我之前评论中的截图了吗?在截图中,你会看到一些来自原始形状的白色边框,这很奇怪。灰色的那个应该覆盖原始绘制,但它没有做到,也许需要一些黑魔法:p - firetrap
我不确定这张图片的问题在哪里,因为我不知道你想要实现什么。你能否发一篇新的问题描述得更好,并展示一些代码和/或XML?你可以在这里放一个链接,这样我也可以在那里帮助你。 - android developer
显示剩余4条评论

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