العمل مع الواجهة في خرائط Google SDK لنظام Android

ستكون هذه المقالة مفيدة لأولئك الذين لم يسبق لهم استخدام Google Maps SDK في عملهم.

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

صورة
مصدر

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

لسوء الحظ ، لا تسمح لك خرائط Google SDK لنظام Android بتغيير موضع أزرار التحكم ، ما يسمى تتضمن عناصر التحكم في واجهة المستخدم ، ما يلي: IndoorLevelPicker - يُظهر مخطط الأرضية للمباني و Compass - بوصلة وزر موقعي - انتقل إلى الخريطة على الموقع الحالي وشريط أدوات الخريطة - أزرار لإنشاء مسار وفتح الخريطة ، وكذلك ZoomControls - قم بزيادة وتقليص مقياس الخريطة.

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

صورة
مشاكل في عرض عناصر تحكم واجهة المستخدم من SDK (مظللة باللون البرتقالي) ونظيراتها المخصصة (مظللة باللون الأخضر)

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

قرار

أول شيء يتعين علينا القيام به هو إخفاء عرض الأزرار الأصلية. يمكنك القيام بذلك عن طريق تخطي طريقة onMapReady ، ويتم استدعاؤها عندما تكون البطاقة جاهزة للاستخدام.

عدم إظهار الأزرار تكبير / تصغير التحكم وعدم إظهار الأزرار إنشاء مسار من SDK
private GoogleMap mMap; private UiSettings uiSettings; @Override public void onMapReady(GoogleMap googleMap) { mMap = googleMap; uiSettings = mMap.getUiSettings(); //   Zoom uiSettings.setZoomControlsEnabled(false); //         SDK uiSettings.setMapToolbarEnabled(false); } 


نضيف الأزرار اللازمة إلى التخطيط ، حيث يجب أن تكون متوافقة مع تصميمنا:

صورة
موقع أزرار التحكم في الخريطة المخصصة

بعد ذلك ، في طريقة onCreateView ، حدد الإجراءات التي يجب أن تحدث عند النقر على الأزرار لدينا:

معالجات للأزرار لزيادة وخفض المقياس ، وكذلك إنشاء مسار
 private ImageButton imageButtonZoomIn; private ImageButton imageButtonZoomOut; private ImageButton imageButtonRoute; private GoogleMap mMap; @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { //  imageButtonZoomIn = view.findViewById(R.id.imageButtonZoomIn); imageButtonZoomIn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mMap.animateCamera(CameraUpdateFactory.zoomIn()); } }); //  imageButtonZoomOut = view.findViewById(R.id.imageButtonZoomOut); imageButtonZoomOut.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mMap.animateCamera(CameraUpdateFactory.zoomOut()); } }); //      imageButtonRoute = view.findViewById(R.id.imageButtonRoute); imageButtonRoute.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String latitude = String.valueOf(activMarker.getPosition().latitude); String longitude = String.valueOf(activMarker.getPosition().longitude); Uri gmmIntentUri = Uri.parse("google.navigation:q=" + latitude + "," + longitude); Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri); mapIntent.setPackage("com.google.android.apps.maps"); try{ if (mapIntent.resolveActivity(Objects.requireNonNull(getActivity()).getPackageManager()) != null) { startActivity(mapIntent); } }catch (NullPointerException e){ Log.e(TAG, "onClick: NullPointerException: Couldn't open map." + e.getMessage() ); Toast.makeText(getActivity(), "Couldn't open map", Toast.LENGTH_SHORT).show(); } } }); } 


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

تنشيط وإلغاء تنشيط أزرار التكبير
  @Override public void onCameraIdle() { if (mMap.getCameraPosition().zoom == mMap.getMinZoomLevel()){ //  ,     imageButtonZoomOut.setEnabled(false); imageButtonZoomIn.setEnabled(true); }else if (mMap.getCameraPosition().zoom == mMap.getMaxZoomLevel()){ //  ,     imageButtonZoomOut.setEnabled(true); imageButtonZoomIn.setEnabled(false); }else { //       imageButtonZoomOut.setEnabled(true); imageButtonZoomIn.setEnabled(true); } } 


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

صورة
أزرار التحكم ماركر

معالجة نقرة على خريطة لإضافة علامة وعرض أزرار التحكم المخصصة
 private GoogleMap mMap; private ImageButton imageButtonRoute; @Override public void onMapReady(GoogleMap googleMap) { mMap = googleMap; mMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() { @Override public void onMapClick(LatLng latLng) { imageButtonRoute.setVisibility(View.GONE); if (myMarker !=null){ //   myMarker.remove(); } //    myMarker = mMap.addMarker(new MarkerOptions() .position(latLng) //   .title(Objects.requireNonNull(getContext()).getString(R.string.title_on_marker_to_new_order)) // true ,         .draggable(true)); myMarker.setTag(null); //    imageButtonAddMarker.setVisibility(View.VISIBLE); imageButtonRemoveMarker.setVisibility(View.VISIBLE); } }); } 


نشير إلى ما نريد أن نفعله بالعلامة عندما نضغط على زر الإضافة
 private ImageButton imageButtonAddMarker; private Marker myMarker; @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { imageButtonAddMarker = view.findViewById(R.id.imageButtonAddMarker); imageButtonAddMarker.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (myMarker !=null && myMarker.getTag()==null) { //    imageButtonAddMarker.setVisibility(View.GONE); imageButtonRemoveMarker.setVisibility(View.GONE); //  ,          listener.openOrderContentsFragmentFromMap(null, myMarker); //   ,     myMarker.remove(); } } }); } 


ميزة أخرى هي أنه لا يوجد زر في SDK لحذف العلامة على البطاقة. للقيام بذلك ، نقوم أيضًا بعمل زر خاص بنا:

حدد ما نريد القيام به باستخدام العلامة عندما نقر على زر حذف العلامة
 private ImageButton imageButtonRemoveMarker; private Marker myMarker; @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { imageButtonRemoveMarker = view.findViewById(R.id.imageButtonRemoveMarker); imageButtonRemoveMarker.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (myMarker !=null && myMarker.getTag()==null){ //   imageButtonAddMarker.setVisibility(View.GONE); imageButtonRemoveMarker.setVisibility(View.GONE); //    myMarker.remove(); } } }); } 


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

قم بتعيين الإجراء عند النقر فوق نافذة معلومات العلامة
 private GoogleMap mMap; @Override public void onMapReady(GoogleMap googleMap) { mMap = googleMap; mMap.setOnInfoWindowClickListener(new GoogleMap.OnInfoWindowClickListener() { @Override public void onInfoWindowClick(Marker marker) { if (marker.getTag()==null) { //    : //      listener.openOrderContentsFragmentFromMap(null, marker); if (myMarker != null) { //   ,     myMarker.remove(); } } else { //     : //         listener.openOrderContentsFragment((Long) marker.getTag()); } } }); } 


لا تختلف عملية إخراج عدة علامات (قراءة قائمة الطلبات) إلى البطاقة من حيث المبدأ عن إخراج علامة واحدة. تتكون العلامة من الإحداثيات (الموضع) ، والعنوان (العنوان) ، والنص الصغير أسفل العنوان (المقتطف) والعلامة (setTag) - يمكن استخدامه لتحديد العديد من العلامات على الخريطة.

صورة
علامات خريطة عدة

رسم علامات متعددة مع إحداثيات معينة
 public void drawListMarker(List<InfoMarker> latLngList) { if (latLngList == null || latLngList.size() == 0) { return; } //    mMap.clear(); LatLngBounds.Builder builder = new LatLngBounds.Builder(); boolean fiarstGreean = true; int count = 1; for (InfoMarker latLng : latLngList) { BitmapDescriptor icon; if (fiarstGreean){ //      //..           icon = BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN); fiarstGreean = false; } else { icon = latLng.getIcon(); } //      mMap.addMarker(new MarkerOptions().position(latLng.getLatLng()).title(String.valueOf(count)).snippet(latLng.getTitle()).icon(icon)).setTag(latLng.getIdOrder()); builder.include(latLng.getLatLng()); count++; } //       CameraUpdate cameraUpdate; if (loaded) { cameraUpdate = CameraUpdateFactory .newLatLngBounds(builder.build(), 100); } else { cameraUpdate = CameraUpdateFactory.newLatLng(builder.build().getCenter()); } mMap.moveCamera(cameraUpdate); mMap.animateCamera(CameraUpdateFactory.zoomIn()); mMap.animateCamera(CameraUpdateFactory.zoomTo(10), 1000, null); } 


بضع كلمات عن الترميز الجغرافي

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

تحتوي خرائط Google SDK على فئة Geocoder ، من خلال استدعاء أسلوب getFromLocation ، يمكنك الحصول على مجموعة من العناوين بالإحداثيات المحددة.

حتى لا يتم حظر مؤشر ترابط واجهة المستخدم لفترة طويلة ، خاصةً إذا كان الإنترنت بطيئًا أو يتعذر الوصول إليه ، بواسطة المكالمات - سنستخدم RxJava:

صورة
عنوان النقطة الناتجة على الخريطة بناءً على الإحداثيات الجغرافية

باستخدام Java RX للوصول إلى Google geododer
 LatLng position = myMarker.getPosition(); Location location = new Location("new"); location.setLatitude(position.latitude); location.setLongitude(position.longitude); LocationRepostiory locationRepostiory = new LocationRepostiory(context, location); locationRepostiory.getLastLocation(). observeOn(SchedulerProvider.getInstance().ui()). subscribeOn(SchedulerProvider.getInstance().computation()). subscribe(locationString -> { if(editTextAddress.length()==0){ //       ,      editTextAddress.setText(locationString); } }, throwable -> { }); 


نص الفئة LocationRepostiory الذي يحدث فيه الترميز الجغرافي العكسي
 public class LocationRepostiory { private Context context; private Location location; public LocationRepostiory(Context context, Location location) { this.context = context; this.location = location; } public Single<String> getLastLocation() { return Single.create(this::subscribeOnLocation); } private void subscribeOnLocation(SingleEmitter<String> e) { Geocoder geocoder = new Geocoder(context, Locale.getDefault()); String errorMessage = ""; List<Address> addresses = null; try { //     addresses = geocoder.getFromLocation( location.getLatitude(), location.getLongitude(), // In this sample, get just a single address. 1); } catch (IOException ioException) { //,      I/O. errorMessage = context.getString(R.string.service_not_available); Log.e(TAG, errorMessage, ioException); } catch (IllegalArgumentException illegalArgumentException) { // ,       errorMessage = context.getString(R.string.invalid_lat_long_used); Log.e(TAG, errorMessage + ". " + "Latitude = " + location.getLatitude() + ", Longitude = " + location.getLongitude(), illegalArgumentException); } //  ,     if (addresses == null || addresses.size() == 0) { if (errorMessage.isEmpty()) { errorMessage = context.getString(R.string.no_address_found); Log.e(TAG, errorMessage); } } else { Address address = addresses.get(0); ArrayList<String> addressFragments = new ArrayList<String>(); //      . for (int i = 0; i <= address.getMaxAddressLineIndex(); i++) { e.onSuccess(address.getAddressLine(i)); } } } } 

Source: https://habr.com/ru/post/ar484100/


All Articles