انقر نقرًا مزدوجًا فوق القفل. دراجة؟

كيف بدأت



مرة أخرى ، بحفر رمز Legacy وكافح مع تسرب السياق ، كسرت قفل النقر المزدوج على الزر في التطبيق. كان علي أن أبحث عن ما كسرته بالضبط وكيف تم تنفيذه. وبالنظر إلى أنه في الأساس لهذا الحظر يُقترح إما تعطيل عنصر واجهة المستخدم نفسه أو ببساطة تجاهل النقرات اللاحقة لفترة قصيرة من الزمن ، بدا لي الحل الحالي مثيرًا للاهتمام من وجهة النظر. كود الربط. ولكن لا يزال يتطلب إنشاء قوائم الأزرار وكتابة الكثير من التعليمات البرمجية الداعمة. أنشئ مثيلًا للفئة التي ستخزن قائمة العناصر ، وملئها ، واستدعاء ثلاث طرق في كل معالج نقرة. بشكل عام ، هناك العديد من الأماكن حيث يمكنك نسيان أو الخلط بين شيء ما. ولا أحب أن أتذكر أي شيء. في كل مرة يبدو لي أنني أتذكر شيئا ، اتضحالتي أتذكرها بشكل غير صحيح ، أو أن شخصًا ما أعاد تصميمها بشكل مختلف.



نترك السؤال حول ما إذا كان من الصواب القيام بذلك أو ما إذا كنا بحاجة ببساطة إلى نقل المعالجات القابلة للتدخل بكفاءة إلى تدفقات الخلفية. سنقوم فقط بصنع دراجة أخرى ، ربما أكثر راحة.



بشكل عام ، جعلني الكسل الطبيعي أفكر ، هل من الممكن الاستغناء عن كل هذا؟ حسنًا ، لحجب الزر والنسيان. وستواصل العمل هناك كما ينبغي. اعتقدت في البداية أنه ربما توجد بالفعل بعض المكتبات التي يمكن توصيلها وأنه يجب استدعاء طريقة واحدة فقط من النوع ، sdelayMneHorosho ().



ولكن مرة أخرى ، أنا شخص بمعنى معين للمدرسة القديمة ، وبالتالي لا أحب أي إدمان غير ضروري. تجعلني حديقة الحيوانات للمكتبات وتوليد الأكواد أشعر باليأس وخيبة الأمل في الإنسانية. حسنًا ، لم يجد googling السطحي سوى خيارات نموذجية مع أجهزة ضبط الوقت أو اختلافاتها.



على سبيل المثال:



One



Two



وهكذا ...



يمكنك أيضًا إيقاف تشغيل العنصر باستخدام السطر الأول في المعالج ، ثم تشغيله. المشكلة الوحيدة هي أنه ليس من السهل دائمًا القيام بذلك لاحقًا ، وفي هذه الحالة من الضروري إضافة مكالمة إلى رمز "التشغيل" في نهاية جميع متغيرات التنفيذ التي يمكن أن تنتج عن الضغط على الزر. ليس من المستغرب أن هذه القرارات لم يتم غوغل على الفور. إنها معقدة للغاية ومن الصعب للغاية الحفاظ عليها.



كنت أرغب في جعلها أبسط وأكثر شمولية ، وتذكر أنه كان ضروريًا بأقل قدر ممكن.



الحل من المشروع



كما قلت ، تم ترتيب الحل الحالي بشكل مثير للاهتمام ، على الرغم من أنه يحتوي على جميع أوجه القصور في الحلول الحالية. على الأقل كانت فئة بسيطة منفصلة. كما سمح بعمل قوائم مختلفة من العناصر غير المتصلة ، على الرغم من أنني لست متأكدًا مما إذا كان هذا منطقيًا.



الفصل الأصلي لحظر النقر المزدوج
public class MultiClickFilter {
    private static final long TEST_CLICK_WAIT = 500;
    private ArrayList<View> buttonList = new ArrayList<>();
    private long lastClickMillis = -1;

    // User is responsible for setting up this list before using
    public ArrayList<View> getButtonList() {
        return buttonList;
    }

    public void lockButtons() {
        lastClickMillis = System.currentTimeMillis();
        for (View b : buttonList) {
            disableButton(b);
        }
    }

    public void unlockButtons() {
        for (View b : buttonList) {
            enableButton(b);
        }
    }

    // function to help prevent execution of rapid multiple clicks on drive buttons
    //
    public boolean isClickedLately() {
        return (System.currentTimeMillis() - lastClickMillis) < TEST_CLICK_WAIT;  // true will block execution of button function.
    }

    private void enableButton(View button) {
        button.setClickable(true);
        button.setEnabled(true);
    }

    private void disableButton(View button) {
        button.setClickable(false);
        button.setEnabled(false);
    }
}


:



public class TestFragment extends Fragment {

	<=======  ========>

	private MultiClickFilter testMultiClickFilter = new MultiClickFilter();

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
		<=======  ========>

		testMultiClickFilter.getButtonList().add(testButton);
		testMultiClickFilter.getButtonList().add(test2Button);

		<=======  ========>

		testButton.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				if (testMultiClickFilter.isClickedLately()) {
					return;
				}

				testMultiClickFilter.lockButtons();
				startTestPlayback(v);
				testMultiClickFilter.unlockButtons();
			}
		});

		test2Button.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				if (testMultiClickFilter.isClickedLately()) {
					return;
				}

				testMultiClickFilter.lockButtons();
				loadTestProperties(v);
				testMultiClickFilter.unlockButtons();
			}
		});

		<=======  ========>
	}
	
	<=======  ========>
}




الفصل صغير ومن حيث المبدأ من الواضح ما يفعله. باختصار ، من أجل منع زر على بعض الأنشطة أو الأجزاء ، تحتاج إلى إنشاء مثيل لفئة MultiClickFilter وملء قائمة عناصر واجهة المستخدم التي يجب حظرها. يمكنك إنشاء عدة قوائم ، ولكن في هذه الحالة ، يجب على معالج كل عنصر "معرفة" مثيل "فلتر النقرات" المراد سحبه.



بالإضافة إلى ذلك ، لا يسمح لك ببساطة بتجاهل النقرة. للقيام بذلك ، يجب عليك بالضرورة حظر قائمة العناصر بالكامل ، وبالتالي ، يجب إلغاء تأمينها. هذا يؤدي إلى رمز إضافي لإضافته إلى كل معالج. نعم، وفي المثال، وأود أن وضع unlockButtons طريقة في النهاية كتلة، لكنك لا تعرف أبدًا ... بشكل عام ، يثير هذا القرار أسئلة.



حل جديد



بشكل عام ، إدراكًا أنه ربما لن يكون هناك نوع من الرصاصة الفضية ، فقد تم قبوله كمقر أولي:



  1. لا يُنصح بفصل قوائم الأزرار القابلة للقفل. حسنًا ، لم أستطع التفكير في أي مثال يتطلب مثل هذا الفصل.
  2. لا تقم بتعطيل عنصر (ممكّن / قابل للنقر) من أجل الحفاظ على الرسوم المتحركة والحيوية العامة للعنصر
  3. منع النقرة في أي معالج مخصص لذلك ، لأن من المفترض أن المستخدم المناسب لا ينقر في أي مكان كما لو كان من مدفع رشاش ، ولمنع "الارتداد" العرضي ، يكفي ببساطة إيقاف معالجة النقرات لعدة مئات من المللي ثانية "للجميع"


لذا ، من الناحية المثالية ، يجب أن يكون لدينا نقطة واحدة في الشفرة حيث تتم جميع المعالجة وطريقة واحدة ستنطلق من أي مكان في المشروع في أي معالج وستحظر معالجة النقرات المتكررة. لنفترض أن واجهة المستخدم الخاصة بنا لا تعني أن المستخدم ينقر أكثر من مرتين في الثانية. لا ، إذا كان ذلك ضروريًا ، فعلى ما يبدو ، سيتعين عليك إيلاء اهتمام خاص للأداء ، لكن حالتنا بسيطة ، بحيث لا يمكن للأصابع التي ترتجف بسرور إسقاط التطبيق على وظيفة غير قابلة للدخول. وأيضًا ، حتى لا تضطر إلى الاستحمام في كل مرة حول تحسين أداء الانتقال البسيط من نشاط إلى آخر أو إطلاق حوار تقدم في كل مرة.



كل هذا سيعمل بالنسبة لنا في الموضوع الرئيسي ، لذلك لا داعي للقلق بشأن المزامنة. أيضًا ، في الواقع ، يمكننا تحويل الشيك إلى ما إذا كنا بحاجة إلى معالجة النقرة أو تجاهلها ، بهذه الطريقة بالذات. حسنًا ، إذا كان يعمل ، فيمكننا جعل فترة الحظر قابلة للتخصيص. لذلك في حالة سيئة للغاية ، يمكنك زيادة الفاصل الزمني لمعالج معين.



هل هو ممكن؟



اتضح أن التنفيذ كان بسيطًا وموجزًا ​​بشكل مدهش.
package com.ai.android.common;

import android.os.Handler;
import android.os.Looper;

import androidx.annotation.MainThread;

public abstract class MultiClickFilter {
    private static final int DEFAULT_LOCK_TIME_MS = 500;
    private static final Handler uiHandler = new Handler(Looper.getMainLooper());

    private static boolean locked = false;

    @MainThread
    public static boolean clickIsLocked(int lockTimeMs) {
        if (locked)
            return true;

        locked = true;

        uiHandler.postDelayed(() -> locked = false, lockTimeMs);

        return false;
    }

    @MainThread
    public static boolean clickIsLocked() {
        return clickIsLocked(DEFAULT_LOCK_TIME_MS);
    }
}


:



public class TestFragment {

	<=======  ========>

	private ListView devicePropertiesListView;

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
		devicePropertiesListView = view.findViewById(R.id.list_view);
		devicePropertiesListView.setOnItemClickListener(this::doOnItemClick);

		<=======  ========>

		return view;
	}

	private void doOnItemClick(AdapterView<?> adapterView, View view, int position, long id) {
		if (MultiClickFilter.clickIsLocked(1000 * 2))
			return;

		<=======  ========>
	}

	<=======  ========>
}




بشكل عام ، تحتاج الآن فقط إلى إضافة فئة MultiClickFilter إلى المشروع والتحقق مما إذا كان محظورًا في بداية كل معالج نقرات:



        if (MultiClickFilter.clickIsLocked())
            return;


إذا كانت النقرة ستتم معالجتها ، فسيتم تعيين قفل للوقت المحدد (أو افتراضيًا). ستتيح لك الطريقة عدم التفكير في قوائم العناصر ، وليس إنشاء عمليات تحقق معقدة وليس إدارة توفر عناصر واجهة المستخدم يدويًا. أقترح مناقشة هذا التنفيذ في التعليقات ، ربما هناك خيارات أفضل؟



All Articles