Android EditText:仅启用滚动但停止点击

Posted

技术标签:

【中文标题】Android EditText:仅启用滚动但停止点击【英文标题】:Android EditText: enable only scrolling but stop comsuming clicks 【发布时间】:2020-11-13 02:24:39 【问题描述】:

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

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

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

代码运行正常:

如果 EditText 有很长的文本:我可以水平滚动文本以阅读全部内容。在文本的末尾,父布局进行滑动并可以更改选项卡 我可以长按 EditText 以启用父布局中的选择复选标记 光标和滑块正确隐藏在 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_
    android:layout_
    android:layout_marginRight="5dp"
    android:layout_marginLeft="5dp" >

    <LinearLayout
        android:layout_
        android:layout_
        android:layout_weight="1"
        android:orientation="vertical" >

        <LinearLayout
            android:layout_
            android:layout_
            android:orientation="horizontal" >

            <ImageView
                android:id="@+id/sync_task_master_icon"
                android:layout_
                android:layout_
                android:layout_gravity="center_vertical|top"
                android:src="@drawable/ic_16_server" />

            <EditText
                android:id="@+id/sync_task_master_info"
                android:layout_
                android:layout_
                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_
            android:layout_
            android:orientation="horizontal" >

            <ImageView
                android:id="@+id/sync_task_direction_image"
                android:layout_
                android:layout_
                android:layout_gravity="center"
                android:scaleType="fitXY"
                android:src="@drawable/arrow_right_enabled" />

            <ImageView
                android:id="@+id/sync_task_target_icon"
                android:layout_
                android:layout_
                android:layout_gravity="top"
                android:src="@drawable/ic_16_server" />

            <EditText
                android:id="@+id/sync_task_target_info"
                android:layout_
                android:layout_
                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);
        
    );

这些都是非常简单的sn-ps代码

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

EditText 技巧修复了所有在 EditTex 框上发生的简单单击事件之外的所有事件。有线,因为长触摸和滑动正确传递。此外,setOnItemClickListener 在单击 EditText 时不会捕获任何事件,但如果它是 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
    
);

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

【问题讨论】:

你检查android:editable="false"了吗? 我刚试过android:editable="false",但没有帮助。 OnClick 事件由 EditText 使用 【参考方案1】:

编辑:查看下一篇文章并进行适当修复以保留原生 ListView 动画

我是这样解决的: 在适配器中:

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

在 MainActivity 中:

public void dispatchSyncTaskListItemClick(SyncTaskItem item, int position) 
  // my actions

这样,我可以将所需的信息(位置、对象项)传递给活动

我愿意接受任何其他建议,只需将点击传播到父 ListView 以便它直接处理它

【讨论】:

【参考方案2】:

正确修复:

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

我之前的回答有一个问题:它没有保留原生 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 可以修复它

【讨论】:

以上是关于Android EditText:仅启用滚动但停止点击的主要内容,如果未能解决你的问题,请参考以下文章

Android键盘将第一个EditText推送到AppBar下,并且仅可以向上滚动

如何仅在正文上启用反弹过度滚动(而不是页眉和页脚)?

在Android中滚动recyclelerview时,Edittext值消失

Android:EditText 重新启用后无法获得焦点

xamarin android c#Edittext自动滚动到底部

列表视图edittext数据在Android中滚动时丢失