首页 > 解决方案 > Android EditText:仅启用滚动但停止点击

问题描述

我正在为一个应用程序项目做贡献,我遇到了这个问题。

我有一个带有 ArrayAdapter 项目列表的 Horizo​​ntal LinearLayout: image -- EditText -- image

我使用 EditText 来启用水平滚动长文本字段,同时禁用光标和单击事件。父布局需要管理项目上的单击事件+水平滑动以更改主视图中的选项卡

代码正常工作:

唯一的问题是 EditText 使用简单的点击事件并且从不将它们传递给父布局。当我单击图像或单击 EditText 的右侧/左侧时,父布局会正确捕获适配器项上的单击操作。如果我直接单击 EditText,则事件被消耗并且永远不会到达父布局 setOnItemClickListener。

这是一个关于它现在如何工作的简短视频。视频中 10 秒位置的点击必须在文本之外进行:https ://www.youtube.com/watch?v=P306p7AcwAI

这是 xml 示例: https ://github.com/PhilZ-cwm6/SMBSync2/blob/philz/SMBSync2/src/main/res/layout/sync_task_item_view.xml

<LinearLayout
    android:id="@+id/profile_list_sync_layout"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_marginRight="5dp"
    android:layout_marginLeft="5dp" >

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:orientation="vertical" >

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal" >

            <ImageView
                android:id="@+id/sync_task_master_icon"
                android:layout_width="20dp"
                android:layout_height="20dp"
                android:layout_gravity="center_vertical|top"
                android:src="@drawable/ic_16_server" />

            <EditText
                android:id="@+id/sync_task_master_info"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:inputType="none"
                android:background="@android:color/transparent"
                android:cursorVisible="false"
                android:clickable="false"
                android:focusable="false"
                android:focusableInTouchMode="false"
                android:singleLine="true"
                android:ellipsize="end"
                android:lines="1"
                android:text="Master"
                android:textAppearance="?android:attr/textAppearanceSmall" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal" >

            <ImageView
                android:id="@+id/sync_task_direction_image"
                android:layout_width="12dp"
                android:layout_height="18dp"
                android:layout_gravity="center"
                android:scaleType="fitXY"
                android:src="@drawable/arrow_right_enabled" />

            <ImageView
                android:id="@+id/sync_task_target_icon"
                android:layout_width="20dp"
                android:layout_height="20dp"
                android:layout_gravity="top"
                android:src="@drawable/ic_16_server" />

            <EditText
                android:id="@+id/sync_task_target_info"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:inputType="none"
                android:background="@android:color/transparent"
                android:cursorVisible="false"
                android:clickable="false"
                android:focusable="false"
                android:focusableInTouchMode="false"
                android:singleLine="true"
                android:ellipsize="end"
                android:lines="1"
                android:text="Target"
                android:textAppearance="?android:attr/textAppearanceSmall" />
        </LinearLayout>
    </LinearLayout>
</LinearLayout>

适配器代码提取: https ://github.com/PhilZ-cwm6/SMBSync2/blob/philz/SMBSync2/src/main/java/com/sentaroh/android/SMBSync2/AdapterSyncTask.java

public class AdapterSyncTask extends ArrayAdapter<SyncTaskItem> {
    final public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        final SyncTaskItem o = getItem(position);

        View v = convertView;
        if (v == null) {
            LayoutInflater vi = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            v = vi.inflate(id, null);
            holder = new ViewHolder();

            holder.tv_row_master = (EditText) v.findViewById(R.id.sync_task_master_info);
            holder.tv_row_target = (EditText) v.findViewById(R.id.sync_task_target_info);
        }

        if (o != null) {
            holder.tv_row_master.setText("source dir path");
            holder.tv_row_master.setInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); //disable auto-correct highlight in EditText

            holder.tv_row_target.setText(destination dir path);
            holder.tv_row_target.setTextColor(mTextColor);
            holder.tv_row_target.setInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); //disable auto-correct highlight in EditText
        }
        return v;
    }

    static class ViewHolder {
        EditText tv_row_master, tv_row_target;
    }
}

以及需要 onClick 事件的 ActivityMain: https ://github.com/PhilZ-cwm6/SMBSync2/blob/philz/SMBSync2/src/main/java/com/sentaroh/android/SMBSync2/ActivityMain.java

private void setSyncTaskListItemClickListener() {
    mGp.syncTaskListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            SyncTaskItem item = mGp.syncTaskAdapter.getItem(position);
            editSyncTask(item.getSyncTaskName(), item.isSyncTaskAuto(), position);
        }
    });
}

这些是非常简单的代码片段

基本上,原始代码与 TextView 一起正常工作,但无法在一行中查看/滚动长文本路径。使用 Horizo​​ntalScrollView 也可以,但不会传递任何事件,如果单击发生在文本字段上,则包括滑动和长按到父级。

当 EditTex 框上发生简单的单击事件时,EditText 技巧可以修复所有这些事件。有线,因为长触摸和滑动正确传递。此外,当单击 EditText 时,setOnItemClickListener 不会捕获任何事件,但如果它是 TextView,则会捕获它们

知道如何解决这个问题吗?我考虑过自定义 EditText,但最终不确定 ArrayAdapter 如何获得 EditText 位置。我是否必须为 EditText 使用第二个适配器?

此致

编辑:我可以将此代码添加到适配器的 getView() 中:

holder.tv_row_target.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        //click events of EditText are captured
    }
});

但是,我无法将它们传播到 ListView 或 Parent 视图,onClick() 这将避免基于 Parent 在单击时应执行的操作对代码进行大量重写

标签: androidandroid-layoutonclickandroid-edittexthorizontalscrollview

解决方案


适当的修复:

我为任何有同样问题的人发布这个。我在这方面看到了很多线程,但没有人提供一个好的解决方案

我之前的回答有一个问题:它不保留原生 ListView onClick 褪色动画

我尝试了 performclick() 但它是一样的。

我终于使用 MotionEvent 完全修复它,将触摸事件传递给列表视图

在适配器中:我为单击和长按设置 onClick 侦听器,并将操作发送到存在 ListView 操作代码的 MainActivity

适配器监听器:

holder.tv_row_target.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        ((ActivityMain) mContext).dispatchSyncTaskListItemClick(o, position);
    }
});

holder.tv_row_target.setOnLongClickListener(new View.OnLongClickListener() {
    public boolean onLongClick(View v) {
        v.setHapticFeedbackEnabled(false);
        ((ActivityMain) mContext).dispatchSyncTaskListLongClick(o, position);
        return true;//notify long touch event is consumed
    }
});

在主要活动中:setOnItemLongClickListenersetOnItemClickListener必须在那里设置。问题是单击 EditText 或处理触摸并执行 ListView 动作和动画的图标

// simulate click on teh ListView item
public void dispatchSyncTaskListItemClick(SyncTaskItem item, int position) {
    long downTime = SystemClock.uptimeMillis();
    long eventTime = SystemClock.uptimeMillis();
    int first_visible_pos = mGp.syncTaskListView.getFirstVisiblePosition();

    View v = mGp.syncTaskListView.getChildAt(position - first_visible_pos);

    float x = v.getX() + 1;//view items start at 0, MotionEvent doesn't handle the UP action at 0
    float y = v.getY();

    //first touch
    MotionEvent motionDown = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_DOWN, x, y, 0);
    motionDown.setSource(InputDevice.SOURCE_TOUCHSCREEN);
    mGp.syncTaskListView.onTouchEvent(motionDown);

    //release touch
    downTime = SystemClock.uptimeMillis();
    eventTime = SystemClock.uptimeMillis();
    MotionEvent motionUp = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0);
    motionUp.setSource(InputDevice.SOURCE_TOUCHSCREEN);
    mGp.syncTaskListView.onTouchEvent(motionUp);

    motionUp.recycle();
    motionDown.recycle();
}

// simulate long touch
public void dispatchSyncTaskListLongClick(SyncTaskItem item, int position) {
    long downTime = SystemClock.uptimeMillis();
    long eventTime = SystemClock.uptimeMillis();
    int first_visible_pos = mGp.syncTaskListView.getFirstVisiblePosition();

    View v = mGp.syncTaskListView.getChildAt(position - first_visible_pos);

    MotionEvent motion = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_DOWN, v.getX(), v.getY(), 0);
    motion.setSource(InputDevice.SOURCE_TOUCHSCREEN);
    mGp.syncTaskListView.onTouchEvent(motion);
    motion.recycle();
}

最困难的部分是x+1因为我的视图从 float x = 0.0 开始,并且直到 > 0 才考虑 UP 动作。不知道为什么?ACTION_DOWN 正确处理

另一件需要注意的是getChildAt:它返回从第一个可见位置开始的视图位置,而不是适配器传递的位置。否则,您会得到一个 null 返回的视图。

现在,它就像与 ListView 交互时一样工作

新编辑:添加v.setHapticFeedbackEnabled(false);删除长触摸重复的触觉反馈!

剩下的问题:我觉得这一切都是一种解决方法。长按时动画触发仍有少许延迟。理想的情况是 EditText 视图完全让 ListView 处理 onClick

所以可能只有自定义 Horizo​​ntalScrollView 或自定义 EditText 可以修复它


推荐阅读