लेख एक साधारण छवि दर्शक के कार्यान्वयन के विवरण का वर्णन करता है और स्क्रॉल कार्यान्वयन और इशारा प्रसंस्करण की कुछ सूक्ष्मताओं को दर्शाता है।
तो, चलिए शुरू करते हैं। हम चित्रों को देखने के लिए एप्लिकेशन विकसित करेंगे। समाप्त आवेदन इस तरह दिखता है (हालांकि स्क्रीनशॉट, निश्चित रूप से, कार्यक्षमता को खराब रूप से व्यक्त करते हैं):
आप एप्लिकेशन को
बाज़ार से या मैन्युअल रूप
से यहां से इंस्टॉल
कर सकते हैं । स्रोत कोड
यहां उपलब्ध
है ।
हमारे आवेदन का मुख्य तत्व ImageViewer वर्ग है, जिसे हम विकसित करेंगे। लेकिन यह भी ध्यान दिया जाना चाहिए कि देखने के लिए एक फ़ाइल का चयन करने के लिए, मैंने पहिया को सुदृढ़ नहीं किया और यहां एक तैयार "घटक" लिया।
एक घटक एक गतिविधि है जिसे मुख्य गतिविधि से शुरू करते समय कहा जाता है। एक फ़ाइल का चयन करने के बाद, हम ImageViewer वर्ग का उपयोग करके इसे अपलोड और प्रदर्शित करते हैं। आइए एक वर्ग पर अधिक विस्तार से विचार करें।
वर्ग व्यू वर्ग का उत्तराधिकारी है और इसके ऑनड्रॉ तरीकों में से केवल एक को ओवरराइड करता है। कक्षा में एक कंस्ट्रक्टर और एक छवि लोड करने की विधि भी शामिल है:
public class ImageViewer extends View { private Bitmap image = null; public ImageViewer(Context context) { super(context); } @Override public void onDraw(Canvas canvas) { if (image != null) canvas.drawBitmap(image, 0, 0, null); } public void loadImage(String fileName) { image = BitmapFactory.decodeFile(fileName); } }
यदि हम स्मार्टफोन की स्क्रीन से बड़ी तस्वीर अपलोड करते हैं, तो उसका केवल एक हिस्सा प्रदर्शित होगा और हमारे पास इसे स्थानांतरित करने या कम करने का कोई तरीका नहीं होगा।
अब स्क्रॉल करने की क्षमता जोड़ते हैं। स्क्रॉलिंग अनिवार्य रूप से एक इशारा है जिसमें उपयोगकर्ता एक उंगली से स्क्रीन को छूता है, इसे बंद किए बिना ले जाता है, और इसे जारी करता है। टच स्क्रीन से संबंधित घटनाओं को संभालने में सक्षम होने के लिए, आपको ऑन-टेक विधि को ओवरराइड करने की आवश्यकता है। विधि MotionEvent प्रकार के एकल पैरामीटर को स्वीकार करती है और यदि घटना संसाधित होती है तो उसे वापस लौटना चाहिए। इस पद्धति के माध्यम से, आप स्क्रॉलिंग सहित किसी भी इशारे के लिए समर्थन लागू कर सकते हैं।
स्क्रॉलिंग को पहचानने के लिए, हमें स्पर्श, गतिमान और विमोचन के क्षण को कैप्चर करना होगा। सौभाग्य से, मैन्युअल रूप से ऐसा करने की आवश्यकता नहीं है, क्योंकि एंड्रॉइड एसडीके में एक वर्ग है जो हमारे लिए सभी काम करता है। इस प्रकार, स्क्रॉलिंग जेस्चर को पहचानने के लिए, हमें अपनी कक्षा को एक प्रकार के GestureDetector के साथ जोड़ना होगा जो कि किसी ऑब्जेक्ट द्वारा प्रारंभ किया गया है जो OnGestureListener इंटरफ़ेस को लागू करता है (यह ऑब्जेक्ट स्क्रॉलिंग इवेंट प्राप्त करेगा)। हमें ImageViewer वर्ग में onTouchEvent विधि को ओवरराइड करने की भी आवश्यकता है और इस प्रकार की घटनाओं के प्रसंस्करण को हमारे OnGestureListener के प्रकार पर पास करना होगा। संशोधित ImageViewer वर्ग (अपरिवर्तित तरीकों के बिना) नीचे प्रस्तुत किया गया है:
public class ImageViewer extends View { private Bitmap image = null; private final GestureDetector gestureDetector; public ImageViewer(Context context) { super(context); gestureDetector = new GestureDetector(context, new MyGestureListener()); } @Override public boolean onTouchEvent(MotionEvent event) { if (gestureDetector.onTouchEvent(event)) return true; return true; } private class MyGestureListener extends SimpleOnGestureListener { @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { scrollBy((int)distanceX, (int)distanceY); return true; } } }
जैसा कि आप देख सकते हैं, हम वास्तव में MyGestureListener को OnGestureListener से नहीं, बल्कि SimpleOnGestureListener से प्राप्त करते हैं। अंतिम वर्ग खाली तरीकों का उपयोग करके OnGestureListener इंटरफ़ेस को लागू करता है। इस तरह, हम खुद को सभी तरीकों के कार्यान्वयन से बचाते हैं, केवल उन्हीं को चुनते हैं जिनकी आवश्यकता होती है।
अब, यदि आप एक बड़ी तस्वीर अपलोड करते हैं, तो हम कम से कम इसे स्क्रॉल करने में सक्षम हो सकते हैं। लेकिन: सबसे पहले, हम तस्वीर से परे स्क्रॉल कर सकते हैं, दूसरा कोई स्क्रॉलबार नहीं है जो हमें बताएगा कि हम कहाँ हैं और कितना ब्रैम के लिए छोड़ दिया गया है।
आइए एक शुरुआत के लिए दूसरी समस्या को हल करें। इंटरनेट पर एक खोज हमें computeHor क्षैतिजScrollRange और computeVerticalScrollRange तरीकों को ओवरराइड करने के लिए ले जाती है। इन विधियों को चित्र के वास्तविक आकार को वापस करना चाहिए (वास्तव में, ऐसे तरीके भी हैं जो स्क्रॉलबार से संबंधित हैं - ये कम्प्यूटरीयरहोसोलरोलएक्स्टेंट हैं, कंप्यूटहोरलोजिकलक्रॉसऑफसेट और एक ऊर्ध्वाधर स्क्रॉलबार के लिए एक ही जोड़ी है। यदि आप उन्हें ओवरराइड करते हैं, तो आप अधिक मनमाना मान लौटा सकते हैं)। लेकिन यह पर्याप्त नहीं है - पहली बार में स्क्रॉलबार को शामिल करने की आवश्यकता है, दूसरे में प्रारंभिक करने के लिए। वे प्रारंभिकहोसक्रोलबार्स विधि द्वारा प्रारंभ किए गए सेटहॉर्गेसॉर्सलक्रॉसबर्न अक्षम और सेटवर्टिकलस्क्रॉलबैर इनेबल्ड विधियों द्वारा शामिल किए गए हैं। लेकिन यहाँ बुरा भाग्य है - अंतिम विधि टाइपएड्रे के थोड़े अस्पष्ट पैरामीटर को लेती है। इस पैरामीटर में दृश्य के लिए मानक विशेषताओं का एक सेट होना चाहिए। सूची को XML विशेषता तालिका में
यहां देखा जा सकता है। यदि हम XML से अपना विचार बनाते हैं, तो एंड्रॉइड रनटाइम स्वचालित रूप से ऐसी सूची संकलित करेगा। लेकिन चूंकि हम कक्षा को प्रोग्रामेटिक रूप से बनाते हैं, इसलिए हमें इस सूची को प्रोग्रामेटिक रूप से बनाने की भी आवश्यकता है। ऐसा करने के लिए, resrs.xml फ़ाइल को निम्न सामग्री के साथ res \ मान निर्देशिका में बनाएँ:
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="View"> <attr name="android:background"/> <attr name="android:clickable"/> <attr name="android:contentDescription"/> <attr name="android:drawingCacheQuality"/> <attr name="android:duplicateParentState"/> <attr name="android:fadeScrollbars"/> <attr name="android:fadingEdge"/> <attr name="android:fadingEdgeLength"/> <attr name="android:fitsSystemWindows"/> <attr name="android:focusable"/> <attr name="android:focusableInTouchMode"/> <attr name="android:hapticFeedbackEnabled"/> <attr name="android:id"/> <attr name="android:isScrollContainer"/> <attr name="android:keepScreenOn"/> <attr name="android:longClickable"/> <attr name="android:minHeight"/> <attr name="android:minWidth"/> <attr name="android:nextFocusDown"/> <attr name="android:nextFocusLeft"/> <attr name="android:nextFocusRight"/> <attr name="android:nextFocusUp"/> <attr name="android:onClick"/> <attr name="android:padding"/> <attr name="android:paddingBottom"/> <attr name="android:paddingLeft"/> <attr name="android:paddingRight"/> <attr name="android:paddingTop"/> <attr name="android:saveEnabled"/> <attr name="android:scrollX"/> <attr name="android:scrollY"/> <attr name="android:scrollbarAlwaysDrawHorizontalTrack"/> <attr name="android:scrollbarAlwaysDrawVerticalTrack"/> <attr name="android:scrollbarDefaultDelayBeforeFade"/> <attr name="android:scrollbarFadeDuration"/> <attr name="android:scrollbarSize"/> <attr name="android:scrollbarStyle"/> <attr name="android:scrollbarThumbHorizontal"/> <attr name="android:scrollbarThumbVertical"/> <attr name="android:scrollbarTrackHorizontal"/> <attr name="android:scrollbarTrackVertical"/> <attr name="android:scrollbars"/> <attr name="android:soundEffectsEnabled"/> <attr name="android:tag"/> <attr name="android:visibility"/> </declare-styleable> </resources>
फ़ाइल बस उन सभी विशेषताओं को सूचीबद्ध करती है जो ऊपर उल्लिखित तालिका में सूचीबद्ध थीं (कुछ को छोड़कर जो कि कंपाइलर एक त्रुटि के रूप में इंगित करता है - जाहिरा तौर पर हाल ही की सूची प्रलेखन में है)। संशोधित ImageViewer वर्ग (अपरिवर्तनीय तरीकों को छोड़कर):
public class ImageViewer extends View { private Bitmap image = null; private final GestureDetector gestureDetector; public ImageViewer(Context context) { super(context); gestureDetector = new GestureDetector(context, new MyGestureListener());
मैं इस पर ध्यान केन्द्रित नहीं करना चाहता, इसलिए आइए हम "फ़्लिंग" इशारे के लिए समर्थन जोड़ें। यह इशारा स्क्रॉल इशारे के लिए एक अतिरिक्त है, लेकिन अंतिम क्षणों में उंगली को स्थानांतरित करने की गति (जारी करने से पहले) को ध्यान में रखा जाता है, और यदि यह शून्य नहीं है, तो स्क्रॉल धीरे-धीरे लुप्त होती है। इस इशारे के लिए समर्थन पहले से ही GestureDetector में शामिल है - इसलिए हमें MyGestureListener वर्ग में ऑनफ्लिंग विधि को ओवरराइड करने की आवश्यकता है। इस घटना को पकड़ने के बाद, हमें स्क्रॉलिंग की स्थिति बदलने के लिए कुछ और समय चाहिए। बेशक, यह टाइमर या कुछ और का उपयोग करके "मैन्युअल रूप से" किया जा सकता है, लेकिन फिर से, एंड्रॉइड एसडीके में पहले से ही एक वर्ग है जो आवश्यक कार्यक्षमता को लागू करता है। इसलिए, आपको ImageViewer वर्ग में Scroller प्रकार का एक और फ़ील्ड जोड़ना होगा, जो "अवशिष्ट" स्क्रॉलिंग से निपटेगा - स्क्रॉलिंग शुरू करने के लिए, आपको इसकी फ़्लिंग विधि को कॉल करने की आवश्यकता है। आपको awakenScrollBars विधि को कॉल करके स्क्रॉलबार (वे छुपाने की आवश्यकता नहीं होने पर) दिखाने की आवश्यकता होती है। और करने के लिए आखिरी बात यह है कि ComputeScroll विधि को फिर से परिभाषित करना है, जो स्क्रॉल स्क्रॉल विधि का उपयोग करके सीधे स्क्रॉल करना चाहिए (Scroller वर्ग स्वयं स्क्रॉल नहीं करता है - यह सिर्फ निर्देशांक के साथ काम करता है)। संशोधित ImageViewer वर्ग के लिए कोड नीचे दिखाया गया है:
public class ImageViewer extends View { private Bitmap image = null; private final GestureDetector gestureDetector; private final Scroller scroller; public ImageViewer(Context context) { super(context); gestureDetector = new GestureDetector(context, new MyGestureListener()); scroller = new Scroller(context);
फ्लिंग इशारा के बारे में बातचीत के अंत में, आपको एक छोटी सी बात करने की ज़रूरत है - जब आप फेंक से स्क्रॉल करते समय अपनी उंगली को छूते हैं, तो आपको स्क्रॉलिंग को रोकने की आवश्यकता होती है। इस बार हम इसे onTouchEvent मेथड में “मैन्युअल” करेंगे। संशोधित विधि नीचे प्रस्तुत की गई है:
@Override public boolean onTouchEvent(MotionEvent event) {
आप पहले से ही काफी दिलचस्प भौतिकी की प्रशंसा कर सकते हैं, लेकिन तस्वीर के बाहर स्क्रॉल करने पर आप कुछ "ग्लिच" देख सकते हैं। यह इस तथ्य के कारण है कि फ्लिंग केवल तस्वीर के भीतर काम करता है, और हर जगह काम करने के बिना स्क्रॉल करना काम करता है। यानी हम तस्वीर से परे तभी जा सकते हैं जब आप बहुत आसानी से स्क्रॉल करें (ताकि फ़्लिंग काम न करे)। आप इस "केंट" को ऑनफ्लिंग विधि में एक प्रोसेसिंग प्रतिबंध लगाकर ठीक कर सकते हैं और केवल रोल की प्रक्रिया कर सकते हैं यदि यह चित्र की सीमाओं से परे नहीं जाता है। संशोधित विधि नीचे प्रस्तुत की गई है:
@Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { boolean scrollBeyondImage = ((getScrollX() < 0) || (getScrollX() > image.getWidth()) || (getScrollY() < 0) || (getScrollY() > image.getHeight())); if (scrollBeyondImage) return false; scroller.fling(getScrollX(), getScrollY(), -(int)velocityX, -(int)velocityY, 0, image.getWidth() - getWidth(), 0, image.getHeight() - getHeight()); awakenScrollBars(scroller.getDuration()); return true; }
अब फिर से हम चित्र से परे स्वतंत्र रूप से स्क्रॉल कर सकते हैं। ऐसा लगता है कि हमने इस समस्या को पहले ही याद कर लिया है ... उसके पास एक सुरुचिपूर्ण समाधान है, जो इस तथ्य में निहित है कि जब आप अपनी उंगली छोड़ते हैं (जब तस्वीर फ्रेम के बाहर स्क्रॉल पूरा हो जाता है) तो आपको तस्वीर को "उचित" स्थान पर आसानी से वापस करने की आवश्यकता होती है। और फिर, हम इस "मैन्युअल रूप से" onTouchEvent विधि में करेंगे:
@Override public boolean onTouchEvent(MotionEvent event) {
अब हम विश्वास के साथ कह सकते हैं कि हमने स्क्रॉलिंग का पता लगा लिया है। हम अंतिम इशारे पर आगे बढ़ सकते हैं जिसे हम लागू करना चाहते हैं - यह एक चुटकी जूम इशारा है।
ओर से, इशारा दो उंगलियों के साथ स्मार्टफोन की स्क्रीन पर किसी काल्पनिक चीज को खींचने या निचोड़ने जैसा लगता है। चरण-दर-चरण इशारा निम्नानुसार होता है: एक उंगली से दबाकर, दूसरी उंगली से दबाकर, एक या दो उंगलियों की स्थिति को बिना छोड़े, दूसरी उंगली को मुक्त करते हुए। ज़ूम की भयावहता को निर्धारित करने के लिए, आपको उस समय उंगलियों के बीच की दूरी के बीच के अनुपात की गणना करनी चाहिए, जिस समय इशारे की शुरुआत होती है और जिस समय इशारा समाप्त होता है। उंगलियों के बीच की दूरी सूत्र sqrt (pow (x2 - X1, 2) + pow (y2 - y1, 2)) द्वारा निर्धारित की जाती है। कुछ स्क्रॉलिंग स्थिति को भी नोट करना आवश्यक है - जिसे सहेजने की आवश्यकता है - क्योंकि यदि आप एक इशारे के साथ छवि को बड़ा करते हैं, तो स्क्रॉलिंग स्थिति बदल जाएगी (रिसाइज्ड छवि के कारण)। यह स्थिति - या यों कहें, जिस बिंदु को आप सहेजना चाहते हैं, उसे Android SDK की शब्दावली में केंद्र बिंदु कहा जाता है, और यह दो उंगलियों के बीच में स्थित है।
आप अपने आप को हमेशा की तरह इशारा को लागू कर सकते हैं, लेकिन सौभाग्य से यह एंड्रॉइड एसडीके में पहले ही लागू हो चुका है (हालांकि केवल संस्करण 2.2 से शुरू हो रहा है)। ScaleGestureDetector वर्ग इसमें मदद करेगा, जिसका उदाहरण हमारी कक्षा में जोड़ा जाएगा। ScaleGestureDetector किसी ऑब्जेक्ट के साथ प्रारंभ होता है, जो OnScaleGestureListener इंटरफ़ेस का समर्थन करता है, इसलिए हम एक आंतरिक वर्ग, MyScaleGestureListener भी बनाते हैं, जो onSaleBegin, onScale और onScaleEnd विधियों को लागू करता है। OnTouchEvent विधि से ScaleGestureDetector पर नियंत्रण स्थानांतरित करना न भूलें। खैर, और सबसे महत्वपूर्ण बात - आपको स्केलिंग डेटा का उपयोग किसी भी तरह करने की आवश्यकता है: आपको उन्हें उन सभी स्थानों पर ध्यान में रखना होगा जहां चित्र की चौड़ाई और ऊंचाई दिखाई देती थी (यानी, आपको वास्तव में स्केलिंग कारक द्वारा इन मापदंडों को गुणा करने की आवश्यकता है)। ImageViewer वर्ग का अंतिम कोड स्रोतों में देखा जा सकता है।
वह सब है। मुझे आशा है कि यह उपयोगी होगा।