Nút trái ở trạng thái được tô sáng bằng touchListener và clickListener


10

Tôi gặp sự cố với Nút của mình ở trạng thái được tô sáng, sau khi thực hiện các thao tác sau:

public class MainActivity extends AppCompatActivity {

    @SuppressLint("ClickableViewAccessibility")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        AppCompatButton button = (AppCompatButton) findViewById(R.id.mybutton);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d("Test", "calling onClick");
            }
        });
        button.setOnTouchListener(new View.OnTouchListener() {

            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN: {
                        v.getBackground().setColorFilter(0xe0f47521,PorterDuff.Mode.SRC_ATOP);
                        v.invalidate();
                        break;
                    }
                    case MotionEvent.ACTION_UP: {
                        v.getBackground().clearColorFilter();
                        v.invalidate();
                        v.performClick();
                        Log.d("Test", "Performing click");
                        return true;
                    }
                }
                return false;
            }
        });

    }
}

Liên quan đến mã ở trên, khi sử dụng nó, tôi hy vọng nút bấm sẽ được xử lý bằng cảm ứng và bằng cách trả về "true", việc xử lý sẽ dừng tại touchListener.

Nhưng đây không phải là trường hợp. Nút vẫn ở trạng thái được tô sáng, ngay cả khi nhấp chuột đang được gọi.

Những gì tôi nhận được là:

Test - calling onClick
Test - Performing click

mặt khác, nếu tôi đang sử dụng đoạn mã sau, nút được nhấp, cùng một bản in, nhưng nút không bị kẹt trong trạng thái được tô sáng:

public class MainActivity extends AppCompatActivity {

    @SuppressLint("ClickableViewAccessibility")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        AppCompatButton button = (AppCompatButton) findViewById(R.id.mybutton);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d("Test", "calling onClick");
            }
        });
        button.setOnTouchListener(new View.OnTouchListener() {

            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN: {
                        v.getBackground().setColorFilter(0xe0f47521,PorterDuff.Mode.SRC_ATOP);
                        v.invalidate();
                        break;
                    }
                    case MotionEvent.ACTION_UP: {
                        v.getBackground().clearColorFilter();
                        v.invalidate();
                        // v.performClick();
                        Log.d("Test", "Performing click");
                        return false;
                    }
                }
                return false;
            }
        });

    }
}

Tôi hơi bối rối khi biết chuỗi phản hồi cho sự kiện chạm. Tôi đoán là vậy:

1) Trình nghe cảm ứng

2) ClickListener

3) Phụ huynh

Ai đó có thể xác nhận điều này là tốt?


Những gì bạn thực sự muốn làm, là nó xử lý bằng cách chạm hoặc thay đổi màu sắc chỉ bằng cách nhấn?
Haider Saleem

Tôi muốn chạy một số logic liên lạc và sau đó gọi biểu diễnClick và để nó không thay đổi màu của nút.
Râu trắng

@Whitebear Vui lòng kiểm tra câu trả lời dưới đây. Có lẽ tôi có thể thêm thông tin.
GensaGames

Điều này có thể giúp bạn hiểu được dòng chảy của các sự kiện chạm. Nó không rõ ràng những gì bạn muốn xảy ra. Bạn có muốn một trình xử lý nhấp chuột và thực hiện nhấp chuột thực hiện? Bạn có muốn nút chuyển từ màu ban đầu sang trạng thái được đặt bởi bộ lọc màu sau đó trở lại màu ban đầu không?
Cheticamp

Hãy để tôi giải thích những gì tôi có ý nghĩa. Tôi có một touchListener và một clickListener trên nút. Cảm ứng trước ưu tiên nhấp chuột và trả về giá trị true nếu nó xử lý sự kiện, điều đó có nghĩa là không ai khác nên xử lý nó. Đây chính xác là những gì tôi đang thực hiện thông qua cảm ứng, xử lý nhấp chuột và trả về đúng sự thật, tuy nhiên nút vẫn được tô sáng ngay cả khi trình nghe onclick được gọi và luồng được thực hiện chính xác.
Râu trắng

Câu trả lời:


10

Các tùy chỉnh như vậy không cần sửa đổi theo chương trình. Bạn có thể làm điều đó một cách đơn giản trong xmlcác tập tin. Trước hết, xóa setOnTouchListenerphương thức mà bạn cung cấp onCreatehoàn toàn. Tiếp theo, xác định màu chọn trong res/colorthư mục như sau. (nếu thư mục không tồn tại, hãy tạo nó)

res / color / button_tint_color.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:color="#e0f47521" android:state_pressed="true" />
    <item android:color="?attr/colorButtonNormal" android:state_pressed="false" />
</selector>

Bây giờ, đặt nó vào app:backgroundTintthuộc tính của nút :

<androidx.appcompat.widget.AppCompatButton
    android:id="@+id/mybutton"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Button"
    app:backgroundTint="@color/button_tint_color" />


Kết quả trực quan:

nhập mô tả hình ảnh ở đây



EDITED: (để giải quyết vấn đề sự kiện cảm ứng)

Từ quan điểm tổng thể, dòng chảy của sự kiện chạm bắt đầu từ Activity , sau đó chảy xuống bố cục (từ bố mẹ đến bố cục con), rồi đến các khung nhìn. (Luồng LTR trong hình sau)

nhập mô tả hình ảnh ở đây

Khi sự kiện liên lạc đạt quan điểm mục tiêu, quan điểm có thể xử lý sự kiện này sau đó quyết định để vượt qua nó để bố trí trước / hoạt động hay không (trở falsevề truetrongonTouch phương thức). (Dòng RTL trong hình trên)

Bây giờ, hãy xem mã nguồn của Chế độ xem để hiểu rõ hơn về các luồng sự kiện chạm. Bằng cách xem xét việc triển khai dispatchTouchEvent, chúng tôi sẽ thấy rằng nếu bạn đặt OnTouchListenerchế độ xem và sau đó quay lại truetrong onTouchphương thức của nó , thìonTouchEvent chế độ xem sẽ không được gọi.

public boolean dispatchTouchEvent(MotionEvent event) {
    // removed lines for conciseness...
    boolean result = false;    
    // removed lines for conciseness...
    if (onFilterTouchEventForSecurity(event)) {
        // removed lines for conciseness...
        ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnTouchListener != null
                && (mViewFlags & ENABLED_MASK) == ENABLED
                && li.mOnTouchListener.onTouch(this, event)) { // <== right here!
            result = true;
        }
        if (!result && onTouchEvent(event)) {
            result = true;
        }
    }
    // removed lines for conciseness...
    return result;
}

Bây giờ, hãy nhìn vào onTouchEventphương thức mà hành động sự kiện là MotionEvent.ACTION_UP. Chúng tôi thấy rằng hành động nhấp chuột thực hiện ở đó. Vì vậy, trở về truetrong OnTouchListener'sonTouch và do đó không gọi onTouchEvent, vì nguyên nhân không gọi OnClickListener'onClick .

Có một vấn đề khác với việc không gọi onTouchEvent, liên quan đến trạng thái ép và bạn đã đề cập trong câu hỏi. Như chúng ta có thể thấy trong khối mã bên dưới, có một phiên bản của các UnsetPressedStatecuộc gọi đó khi nó chạy. Kết quả của việc không gọi là chế độ xem bị kẹt trong trạng thái ép và trạng thái có thể rút ra của nó không thay đổi. setPressed(false)setPressed(false)

public boolean onTouchEvent(MotionEvent event) {
    // removed lines for conciseness...
    if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
        switch (action) {
            case MotionEvent.ACTION_UP:
                // removed lines for conciseness...
                if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                    // removed lines for conciseness...
                    if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
                        // removed lines for conciseness...
                        if (!focusTaken) {
                            // Use a Runnable and post this rather than calling
                            // performClick directly. This lets other visual state
                            // of the view update before click actions start.
                            if (mPerformClick == null) {
                                mPerformClick = new PerformClick();
                            }
                            if (!post(mPerformClick)) {
                                performClickInternal();
                            }
                        }
                    }
                    if (mUnsetPressedState == null) {
                        mUnsetPressedState = new UnsetPressedState();
                    }
                    if (prepressed) {
                        postDelayed(mUnsetPressedState,
                                ViewConfiguration.getPressedStateDuration());
                    } else if (!post(mUnsetPressedState)) {
                        // If the post failed, unpress right now
                        mUnsetPressedState.run();
                    }
                    // removed lines for conciseness...
                }
                // removed lines for conciseness...
                break;
            // removed lines for conciseness...
        }
        return true;
    }
    return false;
}

UnsetPressionState :

private final class UnsetPressedState implements Runnable {
    @Override
    public void run() {
        setPressed(false);
    }
}


Về các mô tả ở trên, bạn có thể thay đổi mã bằng cách setPressed(false)tự gọi mình để thay đổi trạng thái có thể rút được trong đó hành động sự kiện là MotionEvent.ACTION_UP:

button.setOnTouchListener(new View.OnTouchListener() {

    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                v.getBackground().setColorFilter(0xe0f47521,PorterDuff.Mode.SRC_ATOP);
                v.invalidate();
                break;
            }
            case MotionEvent.ACTION_UP: {
                v.getBackground().clearColorFilter();
                // v.invalidate();
                v.setPressed(false);
                v.performClick();
                Log.d("Test", "Performing click");
                return true;
            }
        }
        return false;
    }
});

Đây không phải là những gì tôi đang tìm kiếm bạn của tôi. Tôi đang tìm hiểu sự thay đổi trong hành vi trong cả hai tình huống tôi đã mô tả ở trên. Nếu có điều gì đó tôi có thể giải thích, xin vui lòng cho tôi biết. Tôi không muốn xóa trình xử lý cảm ứng cũng như trình xử lý nhấp chuột. Vui lòng kiểm tra bình luận của tôi để trả lời ở trên.
Râu trắng

@Whitebear: Tôi đã cập nhật câu trả lời. Vui lòng kiểm tra xem, anh bạn.
aminography

Đó là một câu trả lời chi tiết tốt, và tôi chấp nhận nó. Một vài gợi ý để thay đổi: Now, look at the onTouchEvent method where the event action is MotionEvent.ACTION_UP. We see that perform-click action happens there. So, returning true in the OnTouchListener's onTouch and consequently not calling the onTouchEvent, causes not calling the OnClickListener's onClick.Trong trường hợp của tôi, onClick được gọi. mUnsetPressionState kiểm tra xem nó có rỗng không trước khi đặt thành false và cũng không thể chạy được nếu chúng ta bị chặn. Tôi hoàn toàn không hiểu làm thế nào bạn khấu trừ rằng nó nên được đặt thành sai
Râu trắng

Các onClickđược gọi là bởi vì bạn đang gọi v.performClick();. Vui lòng kiểm tra lại đoạn mã trên trong MotionEvent.ACTION_UPphần một lần nữa, setPressed(false)được gọi bằng mọi cách, dù mUnsetPressedStatecó null hay không, có prepressedđúng hay không. Sự khác biệt là trong cách gọi setPressed(false)có thể thông qua post/ postDelayedhoặc trực tiếp.
aminography

2

Bạn đang lộn xộn xung quanh touchfocuscác sự kiện. Hãy bắt đầu với sự hiểu biết hành vi với cùng màu. Theo mặc định, được Selectorgán làm nền cho ButtonAndroid. Vì vậy, chỉ cần thay đổi màu nền, thực hiện là tĩnh (màu sẽ không thay đổi). Nhưng đó không phải là một hành vi bản địa.

Selector có thể trông như thế này

<?xml version="1.0" encoding="utf-8"?> 
  <selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:state_focused="true"
        android:state_pressed="true"
        android:drawable="@drawable/bgalt" />

    <item
        android:state_focused="false"
        android:state_pressed="true"
        android:drawable="@drawable/bgalt" />

    <item android:drawable="@drawable/bgnorm" />
</selector>

Như bạn có thể thấy ở trên, có nhà nước focusedvà nhà nước pressed. Bằng cách cài đặt, onTouchListenerbạn sẽ xử lý các sự kiện chạm, không liên quan gìfocus .

Selectorcủa Nút sẽ thay thế focussự kiện bằng touchtrong khi sự kiện nhấp vào nút. Nhưng trong phần đầu tiên của mã, bạn đã chặn các sự kiện cho touch(trả về true từ cuộc gọi lại). Thay đổi màu sắc không thể tiến hành thêm và đóng băng với cùng màu. Và đó là lý do tại sao biến thể thứ hai (không có đánh chặn) hoạt động tốt và đó là sự nhầm lẫn của bạn.

CẬP NHẬT

Tất cả bạn cần làm là để thay đổi hành vi và màu sắc cho Selector. Dành cho người cũ bằng cách sử dụng nền tảng tiếp theo cho Button. loại bỏ onTouchListenerkhỏi thực hiện của bạn ở tất cả.

<?xml version="1.0" encoding="utf-8"?> 
  <selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:state_pressed="true"
        android:drawable="@color/color_pressed" />

    <item android:drawable="@color/color_normal" />
</selector>

Làm thế nào bạn sẽ thay đổi ví dụ đầu tiên để làm việc tốt sau đó?
Râu trắng

Tôi không tìm cách để loại bỏ người nghe cảm ứng từ thực hiện của tôi. vì tôi muốn bắt các nhấp chuột trên một chế độ xem cụ thể bằng trình xử lý cảm ứng, sau đó tự xử lý chúng (sử dụng PerformanceClick) và trả về true từ trình nghe cảm ứng để nói với các trình xử lý khác rằng không xử lý thêm nữa. Các bản ghi được in tốt trong ví dụ của tôi, nhưng nút vẫn được tô sáng.
Râu trắng

@Whitebear Bạn đã không đề cập đến điều đó trong các câu hỏi của bạn. Bằng mọi cách, bạn có thể sử dụng bao nhiêu onTouchListeners tùy thích. Bạn không cần phải tiêu thụ sự kiện, bởi return true.
GensaGames

@Whitebear HOẶC Xóa bộ chọn và đặt màu thô cho nút qua backgroundColor.
GensaGames

các bạn, bạn vẫn không giải quyết những gì tôi đã viết trong bài viết gốc ..
Whitebear

0

nếu bạn gán nền cho nút, nó sẽ không thay đổi màu khi nhấp.

 <color name="myColor">#000000</color>

và đặt nó là backgrount cho nút của bạn

android:background="@color/myColor"

0

bạn chỉ có thể sử dụng Chips vật liệu thay vì xem Nút. tham khảo: https : // m Material.io/develop/android/components/chip ở đó họ xử lý các sự kiện hililghted đó và bạn có thể tùy chỉnh với việc áp dụng các chủ đề.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.