English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
المقدمة
في تطوير Android، يكون View دائمًا نقطة ضعف لدى مطوري Android، من جهة يرغبون في التقدم، ومن جهة أخرى يخشون التقدم، يمكن القول إن View في Android هو أكبر عائق في طريق التقدم، لأنه يتضمن الكثير من الأمور، مثل ما سنكتبه في هذا المقال من تحريك View، بالإضافة إلى نقل أحداث اللمس في View، إنشاء View مخصص، والتي تعد مشكلات مهمة للغاية ويجب مواجهتها. ولكن بغض النظر عن ذلك، فإن الصعوبات التي لا يتم التغلب عليها الآن ستكون هناك صعوبات للغاية في المستقبل.
قبل ذلك، دعونا نتعرف على قواعد تعريف نظام الأعمدة في Android ومعاملات موقع View بعضها البعض.
مستوى Android
وضع View و حجمه يتم تحديدهما بواسطة أربعة معاملات، وهي left،top،right،bottom،وكلها تعتمد على View الأم.
int width = right-left; int height = bottom-top;
بعد إكمال التخطيط في Activity، يمكننا الحصول على معلومات هذه المعاملات من بعض طرق View:
//الحصول على معاملات left, top, right, bottom int left = getLeft(); int top = getTop(); int right = getRight(); int bottom = getBottom();
بالإضافة إلى ذلك، تم إضافة x،y،translationX،translationY وما إلى ذلك بعد Android 3.0. (x,y) يعني قيم x و y في الزاوية العلوية اليسرى من ViewGroup، وtranslationX،translationY تستخدم لتحريك View. افتراضياً تكون جميعها 0، وتتغير بعد استدعاء setTranslationX()/setTranslationY() لـ View.
//الحصول على معاملات x, y, translationX, translationY int x = getX(); int y = getY(); int translationX = getTranslationX(); int translationY = getTranslationY();
البيان: استدعاء طرق setTranslationX() و setTranslationY() من View يمكن أن يجعل View يتحرك بمسافة معينة، ولكن هذه العملية تتم بشكل فوري. لجعل تحرك View أكثر سلاسة، يمكن استخدام حركة التحكم في الخصائص من View لتعيين translationX و translationY.
ObjectAnimator valueAnimator = ObjectAnimator.ofFloat(textView, "translationX", 200); valueAnimator.setDuration(2000); valueAnimator.start();
إذا تم تعيين setTranslationX() وsetTranslationY() لـ View، فإنه إذا لم تتغير القيم المحددة، فإنه سيتم تحريكه مرة واحدة فقط، أي المسافة المتحركة المحددة في المرة الأولى. بعد مراجعة الشيفرة، وجدنا السبب: بعد تعيين القيم، يقارن النظام القيم المحددة بالـ translationX، translationY الحالية، ويتم التحرك فقط إذا كانت القيم مختلفة.
بعد فهم بعض المعلمات الأساسية لـ View، سنرى ثلاث طرق تحرك View.
استخدام طرق scrollTo()/scrollBy() التي يقدمها نظام Android للتحرك في View.
سواء كان scrollTo() أو scrollByً، فإن طبيعتهما هي تحريك محتوى View/ViewGroup، وتمامًا ما هي عملية تحريكها تتم في اللحظة، لذا من أجل تحقيق تأثير تحرك أفضل، يجب استخدامها مع فئة Scroller. بالإضافة إلى ذلك، إنها تختلف عن Translation السابقة، حيث تحرك View نفسه، وهو أمر يجب فهمه بشكل جيد.
scrollTo() وscrollBy() كلاهما طرق في View، وليس في Scroller، ولكن التحكم في تحريك View بسهولة لا يمكن الفصل عن فئة Scroller.
scrollTo() : يشير إلى تحريك الموقع المطلق، إذا لم يتغير الموقع، فإن التكرار المتكرر لن يكون له تأثير.
شكل عملية تحرك scrollTo
scrollBy() : إن طبيعته هي دائمًا ما تكون استدعاء scrollTo()، مما يعني تحريك المسافة النسبية الحالية (في كل مرة يتم إضافة الموقع الحالي والمسافة المحددة ثم استدعاء scrollTo()، لذا إذا استدعيتها عدة مرات، ستجد أن كل مرة تتحرك بنفس المسافة، وهو ما يميزها عن scrollTo())
شكل عملية تحرك scrollBy
النقطة الإضافية:بشأن الصورتين السابقتين، في الواقع، لم أفهم بالكامل ما هو النسبي والأبدي طوال الوقت، لذا قد تكون الصور اليدوية أسهل في الفهم. بالإضافة إلى ذلك، هناك مشكلة في اتجاهات scrollTo() وscrollBy()، لقد رسمنا نظام الإحداثيات الخاص بـ Android، حيث يمثل x من اليسار إلى اليمين إيجابيًا، وy من الأعلى إلى الأسفل إيجابيًا. ولكن هذا لا ينطبق على scrollTo() وscrollBy()، حيث يكون scrollTo() وscrollBy() عكس ذلك، أي x من اليسار إلى اليمين سلبيًا، وy من الأعلى إلى الأسفل سلبيًا، وهو شيء يبدو كأنه مخادع للغاية.
تحليل فئة Scroller: لماذا يمكن استخدام طرق فئة Scroller للتحرك في محتوى View/ViewGroup؟ سنجرب تحليلًا قريبًا.
أولاً
سنقوم بإنشاء عميل Scroller class mScroller.
ثم
لجعل View يتحرك إلى الموضع المحدد في وقت محدد، سنستدعي طريقة startScroll()، startScroll() هي طريقة في Scroller class، هناك أيضًا طريقة filing() في Scroller class وهي أيضًا شائعة جدًا، وهي تتعامل مع التحرك السلس، عادةً لإنشاء تأثير الإنعكاس بعد التحريك، مما يجعل تحريك View أكثر واقعية. سنرى كود startScroll() الآن:
// يتلقي أربعة/خمسة معلمات. إذا لم يتم تعيين duration، فإنه سيكون قياسيًا. هذه المعلمات ليست صعبة الفهم، لذا لن أشرحها هنا. public void startScroll(int startX, int startY, int dx, int dy, int duration) { ... }
عادةً، بعد استدعاء هذه الطريقة، نذهب إلى استدعاء invalidate() لـ View، هذه الطريقة يمكن أن تطلق طريقة draw() لـ View. وتم استدعاء computeScroll() في draw()، ونحن نجد في الكود المصدر أن computeScroll() هو طريقة فارغة، وهذا هو السبب في أننا بحاجة إلى إعادة كتابة طريقة computeScroll(). لأن عملية التحريك الفعلية تتم في computeScroll().
@Override public void computeScroll() { if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); // يجب استدعاء postInvalidate()/invalidate() لـ View، وإلا فإن تحريك View سيكون فقط في اللقطة الأولى. postInvalidate(); } super.computeScroll(); }
نحن نرى في Scroller class أن هناك أيضًا طريقة computeScrollOffset()، وما هو عملها؟ وظيفتها الرئيسية هي تحديد ما إذا كان mCurrX و mCurrY قد تغير، إذا كان الأمر كذلك، فسيتم العودة إلى true، وإذا لم يكن كذلك، فسيتم العودة إلى false. يمكن للتحكيم من خلال هذه الطريقة تحديد ما إذا كان يجب استمرار استدعاء scrollTo() للتحرك في View. سأقدم أيضًا مثالًا هنا، باستخدام scrollTo() لتحريك View مع حركة الإصبع:
public class CuView extends LinearLayout { private float mStartX; private float mStartY; private Scroller mScroller; /** * اللمس الأولي هل انتهى؟ */ private boolean isFirstFinish; public CuView(Context context) { super(context); init(context); } public CuView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } private void init(Context context) { mScroller = new Scroller(context); } public CuView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public CuView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(context); } /** * دع View تتبع أصابعك * @param event * @return */ @Override public boolean onTouchEvent(MotionEvent event) { int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: /** * بعد اكتمال التحرك الأول، لا نحتاج إلى الحصول على موقع البداية مرة أخرى، وإلا سيؤدي ذلك إلى تحريك View من البداية. */ if (!isFirstFinish) { mStartX = event.getRawX(); mStartY = event.getRawY(); } break;} case MotionEvent.ACTION_MOVE: scrollTo((int) (mStartX - event.getRawX()), (int) (mStartY - event.getRawY())); break;} case MotionEvent.ACTION_UP: //التحرك الأول اكتمال isFirstFinish = true; break;} } return true; } /** * اختبار startScroll */ public void startScroll() { /** * ملاحظة اتجاه حركة Scroller، */ mScroller.startScroll(20, 20, -500, -500, 5000); invalidate(); } @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); invalidate(); } super.computeScroll(); } }
ثانيًا، استخدم الرسوم المتحركة لتحريك View.
هذا يشمل Animate/Frame Animation لـ View، بالإضافة إلى Property Animation التي تم إضافتها بعد الإصدار 3.0. يتم تحريك صورة View فقط، لم يتم تغيير موقع أو حجم View نفسه.
ثالثًا، تعيين LayoutParams لتحريك View
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) textView.getLayoutParams(); layoutParams.leftMargin = 50; textView.requestLayout();
الخاتمة
بهذا تكون نهاية محتوى تلخيص 3 طرق تحريك Android View، آمل أن تكون محتويات هذا المقال مفيدة لكم أثناء تطوير Android، إذا كان لديكم أي استفسارات، يمكنكم ترك تعليقات للتفاعل.