2022-09-01

Android 仿微信聊天位置选择(基于高德地图,BottomSheetBehavior)


废话不多说,直接上图


演示效果



和微信效果差别不大

编辑器不会用,



    xmlns:app="http://schemas.android.com/apk/res-auto"

    xmlns:tools="http://schemas.android.com/tools">

        android:id="@+id/cl_chouti"

        android:layout_width="match_parent"

        android:layout_height="match_parent"

        android:background="@color/colorWhite"

        tools:background="@color/colorAppNormal">

            android:layout_width="match_parent"

            android:layout_height="match_parent"

            android:background="@color/colorTransparent">

                android:layout_width="match_parent"

                android:layout_height="match_parent"

                android:orientation="vertical">

                    android:layout_width="match_parent"

                    android:layout_height="0dp"

                    android:layout_weight="1">

                        android:id="@+id/map"

                        android:layout_width="match_parent"

                        android:layout_height="match_parent" />

                    android:id="@+id/bottomPlaceView"

                    android:layout_width="match_parent"

                    android:layout_height="@dimen/dp_260" />

                android:id="@+id/titleBar"

                android:layout_width="match_parent"

                android:layout_height="@dimen/dp_68"

                android:background="@drawable/shape_gray_gradient_top"

                app:layout_constraintTop_toTopOf="parent"

                app:title_statusBarLightMode="false"

                app:title_titleMainText="" />

                android:id="@+id/tvCancel"

                android:layout_width="wrap_content"

                android:layout_height="wrap_content"

                android:padding="@dimen/dp_10"

                android:text="取消"

                android:textColor="@color/colorWhite"

                android:textSize="@dimen/sp_16"

                android:textStyle="bold"

                android:onClick="onClick"

                app:layout_constraintBottom_toBottomOf="@+id/rtvSure"

                app:layout_constraintStart_toStartOf="parent"

                app:layout_constraintTop_toTopOf="@+id/rtvSure" />

                android:id="@+id/rtvSure"

                android:layout_width="wrap_content"

                android:layout_height="wrap_content"

                android:layout_marginEnd="@dimen/dp_10"

                android:layout_marginBottom="@dimen/dp_28"

                android:paddingHorizontal="@dimen/dp_10"

                android:paddingVertical="@dimen/dp_4"

                android:onClick="onClick"

                android:text="发送"

                android:textColor="@color/colorWhite"

                android:textSize="@dimen/sp_14"

                app:layout_constraintBottom_toBottomOf="@+id/titleBar"

                app:layout_constraintEnd_toEndOf="parent"

                app:rv_backgroundColor="@color/colorAppNormal"

                app:rv_backgroundPressedColor="@color/colorAppPress"

                app:rv_radius="@dimen/dp_4" />

            android:id="@+id/llBottomMenuPanel"

            android:layout_width="match_parent"

            android:layout_height="550dp"

            android:layout_alignParentBottom="true"

            android:background="@drawable/shape_round_top_20_white"

            android:orientation="vertical"

            app:behavior_hideable="false"

            app:behavior_peekHeight="280dp"

            app:behavior_fitToContents="true"

            app:layout_behavior="@string/bottom_sheet_behavior">

                android:id="@+id/viewCloseHeader"

                android:layout_width="match_parent"

                android:layout_height="0dp"

                android:paddingHorizontal="@dimen/dp_10"

                tools:layout_height="@dimen/dp_36">

                    android:id="@+id/ivCloseBottom"

                    android:layout_width="@dimen/dp_50"

                    android:layout_height="@dimen/dp_24"

                    android:layout_marginTop="@dimen/dp_10"

                    android:onClick="onClick"

                    android:background="@drawable/shape_round_10_gray2"

                    android:src="@mipmap/drop_down_unselected_icon"

                    android:tint="@color/color666666"

                    app:layout_constraintEnd_toEndOf="parent"

                    app:layout_constraintStart_toStartOf="parent"

                    app:layout_constraintTop_toTopOf="parent" />

                android:id="@+id/llContentPanel"

                android:layout_width="match_parent"

                android:layout_height="0dp"

                android:layout_weight="1"

                android:orientation="vertical">

                    android:layout_width="match_parent"

                    android:layout_height="@dimen/dp_30"

                    android:layout_marginTop="@dimen/dp_14"

                    android:layout_marginBottom="@dimen/dp_10"

                    android:paddingHorizontal="@dimen/dp_10">

                        android:id="@+id/llSearchPlace"

                        android:layout_width="match_parent"

                        android:layout_height="match_parent"

                        android:background="@drawable/shape_round_5_gray2"

                        android:gravity="center"

                        android:orientation="horizontal"

                        android:visibility="visible"

                        android:onClick="onClick"

                        app:layout_constraintTop_toTopOf="parent">

                            android:layout_width="@dimen/dp_18"

                            android:layout_height="@dimen/dp_18"

                            android:src="@mipmap/ic_app_search"

                            android:tint="@color/color999999" />

                            android:layout_width="wrap_content"

                            android:layout_height="wrap_content"

                            android:layout_marginStart="@dimen/dp_6"

                            android:text="搜索地点"

                            android:textColor="@color/color999999"

                            android:textSize="@dimen/sp_13" />

                        android:id="@+id/llSearchInput"

                        android:layout_width="match_parent"

                        android:layout_height="match_parent"

                        android:focusable="true"

                        android:focusableInTouchMode="true"

                        android:gravity="center"

                        android:orientation="horizontal"

                        android:visibility="gone"

                        app:layout_constraintTop_toTopOf="parent">

                            android:id="@+id/etSearchContent"

                            android:layout_width="0dp"

                            android:layout_height="match_parent"

                            android:layout_weight="1"

                            android:background="@drawable/shape_round_5_gray2"

                            android:drawableStart="@mipmap/ic_app_search"

                            android:drawablePadding="@dimen/dp_6"

                            android:drawableTint="@color/color999999"

                            android:gravity="center_vertical"

                            android:hint="搜索地点"

                            android:paddingHorizontal="@dimen/dp_10"

                            android:singleLine="true"

                            android:textSize="@dimen/sp_13" />

                            android:id="@+id/tvSearchCancel"

                            android:layout_width="@dimen/dp_40"

                            android:layout_height="match_parent"

                            android:layout_marginStart="@dimen/dp_6"

                            android:gravity="center_vertical|right"

                            android:text="取消"

                            android:onClick="onClick"

                            android:textColor="@color/color666666"

                            android:textSize="@dimen/sp_15" />

                    android:layout_width="match_parent"

                    android:layout_height="match_parent">

                        android:id="@+id/loadingLayout"

                        android:layout_width="match_parent"

                        android:layout_height="match_parent"

                        app:isFirstVisible="true">

                            android:id="@+id/refreshLayout"

                            android:layout_width="match_parent"

                            android:layout_height="match_parent"

                            app:srlEnableLoadMore="true"

                            app:srlEnableRefresh="false">

                                android:id="@+id/rvPoiList"

                                android:layout_width="match_parent"

                                android:layout_height="match_parent"

                                android:background="@color/colorBackground"

                                android:overScrollMode="never"

                                android:nestedScrollingEnabled="true"

                                android:scrollbars="none"

                                tools:listitem="@layout/item_map_poi" />



public class ChooseMapActivityextends BaseActivityimplements View.OnClickListener {

private AMapaMap;

private BottomSheetBehaviorbehavior;

private int bottomPanelCollapsedHeight;

private int bottomPanelDifferenceHeight;

private int closeHeaderHeight;

private float lastSlide =0;

private LinearLayout.LayoutParamsbottomPlaceLayoutParams;

private LinearLayout.LayoutParamscloseHeaderLayoutParams;

private MarkerscreenMarker =null;

private MyLocationStylemyLocationStyle;

private boolean isMoveFromUserCheckItem;

private RecyclerCommonAdapterpoiAdapter;

private PoiItemV2currentPoi;

private KeyboardUtils.OnSoftInputChangedListenersoftInputChangedListener = height -> {

LogUtils.e("OnSoftInputChangedListener", height);

if (height >0) {

if (viewBinding.llSearchInput.getVisibility() == View.VISIBLE) {

if (behavior.getState() != BottomSheetBehavior.STATE_EXPANDED) {

viewBinding.etSearchContent.requestFocus();

behavior.setState(BottomSheetBehavior.STATE_EXPANDED);

}

}

}else {

if (viewBinding.llSearchInput.getVisibility() == View.VISIBLE) {

viewBinding.llSearchInput.requestFocus();

}

}

};

@Override

    protected void initView(Bundle savedInstanceState) {

KeyboardUtils.registerSoftInputChangedListener(this,softInputChangedListener);

bottomPanelCollapsedHeight = SizeUtils.dp2px(260);

bottomPanelDifferenceHeight = SizeUtils.dp2px(270);

closeHeaderHeight = SizeUtils.dp2px(36);

AMapLocationClient.updatePrivacyShow(mContext,true,true);

AMapLocationClient.updatePrivacyAgree(mContext,true);

viewBinding.map.onCreate(savedInstanceState);// 此方法必须重写

        if (aMap ==null) {

aMap =viewBinding.map.getMap();

}

setUpMap();

behavior = BottomSheetBehavior.from(viewBinding.llBottomMenuPanel);

behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);

bottomPlaceLayoutParams = (LinearLayout.LayoutParams)viewBinding.bottomPlaceView.getLayoutParams();

closeHeaderLayoutParams = (LinearLayout.LayoutParams)viewBinding.viewCloseHeader.getLayoutParams();

behavior.addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {

@Override

            public void onStateChanged(@NonNull View bottomSheet,int newState) {

if (newState == BottomSheetBehavior.STATE_COLLAPSED) {

LogUtils.e("BottomSheetBehavior.BottomSheetCallback:onStateChanged:STATE_COLLAPSED:: ", newState);

if (StringUtils.isTrimEmpty(viewBinding.etSearchContent.getText().toString())) {

viewBinding.llSearchPlace.setVisibility(View.VISIBLE);

viewBinding.llSearchInput.setVisibility(View.GONE);

}else {

viewBinding.llSearchInput.requestFocus();

}

addMarkerInScreenCenter();

}else {

LogUtils.e("BottomSheetBehavior.BottomSheetCallback:onStateChanged:STATE_HIDDEN:: ", newState);

addMarkerInScreenCenter();

}

}

@Override

            public void onSlide(@NonNull View view,float slide) {

if (slide ==lastSlide)return;

LogUtils.e("BottomSheetBehavior.BottomSheetCallback:onSlide:: ",lastSlide, slide,lastSlide > slide ?"向下减小高度" :"向上增加高度");

bottomPlaceLayoutParams.height = (int) (bottomPanelCollapsedHeight +bottomPanelDifferenceHeight * slide);

viewBinding.bottomPlaceView.setLayoutParams(bottomPlaceLayoutParams);

closeHeaderLayoutParams.height = (int) (closeHeaderHeight * slide);

viewBinding.viewCloseHeader.setLayoutParams(closeHeaderLayoutParams);

lastSlide = slide;

}

});

viewBinding.rvPoiList.setLayoutManager(new LinearLayoutManager(mContext));

viewBinding.rvPoiList.addItemDecoration(new SpacesItemDecoration() {

@Override

            public int getSpaceTop(int position,int itemCount) {

return position !=0 ? SizeUtils.dp2px(1) :0;

}

});

viewBinding.rvPoiList.setAdapter(poiAdapter =new RecyclerCommonAdapter(mContext, R.layout.item_map_poi,new ArrayList<>()) {

@Override

            protected void convert(RecyclerViewHolder holder, PoiItemV2 item,int position) {

holder.setText(R.id.tvItemTitle, item.getTitle());

float distance = AMapUtils.calculateLineDistance(new LatLng(lastPoint.getLatitude(),lastPoint.getLongitude()),new LatLng(item.getLatLonPoint().getLatitude(), item.getLatLonPoint().getLongitude()));

String format = distance >1000 ?"%.1fkm | %s" :"%.1fm | %s";

holder.setText(R.id.tvItemDescription, String.format(format, distance >1000 ? distance /1000 : distance, item.getSnippet() +""));

CheckView cvItemCheck = holder.getView(R.id.cvItemCheck);

if (currentPoi !=null &¤tPoi.getPoiId().equals(item.getPoiId())) {

cvItemCheck.setVisibility(View.VISIBLE);

cvItemCheck.setColor(ContextCompat.getColor(mContext, R.color.colorAppNormal));

}else {

cvItemCheck.setVisibility(View.INVISIBLE);

}

}

});

poiAdapter.setOnItemClickListener((view, holder, position) -> {

if (NoDoubleClickUtils.isDoubleClick())return;

PoiItemV2 itemV2 =poiAdapter.getData().get(position);

if (currentPoi !=null &¤tPoi.getPoiId().equals(itemV2.getPoiId()))return;

currentPoi = itemV2;

poiAdapter.notifyDataSetChanged();

isMoveFromUserCheckItem =true;

viewBinding.map.setEnabled(false);

aMap.moveCamera(CameraUpdateFactory.newCameraPosition(new CameraPosition(new LatLng(itemV2.getLatLonPoint().getLatitude(), itemV2.getLatLonPoint().getLongitude()),18,30,30)));

});

viewBinding.refreshLayout.setEnableOverScrollDrag(false);

viewBinding.refreshLayout.setEnableOverScrollBounce(false);

viewBinding.refreshLayout.setNestedScrollingEnabled(false);

viewBinding.refreshLayout.setEnableNestedScroll(false);

//viewBinding.loadingLayout.setNestedScrollingEnabled(true);

        viewBinding.loadingLayout.showState(LoadingState.class);

/* viewBinding.loadingLayout.setOnReloadListener((v, reloadCode) -> {

currentPage = 0;

doSearch();

});*/

        viewBinding.refreshLayout.setOnLoadMoreListener(refreshLayout -> {

currentPage++;

doSearch();

});

/*aMap.setOnMapLoadedListener(new AMap.OnMapLoadedListener() {

@Override

public void onMapLoaded() {

addMarkerInScreenCenter();

aMap.moveCamera(CameraUpdateFactory.newCameraPosition(new CameraPosition(aMap.getCameraPosition().target, 18, 30, 30)));

currentPage = 0;

lastPoint = new LatLonPoint(aMap.getCameraPosition().target.latitude, aMap.getCameraPosition().target.longitude);

doSearch();

}

});*/

// 设置可视范围变化时的回调的接口方法

        aMap.setOnCameraChangeListener(new AMap.OnCameraChangeListener() {

@Override

            public void onCameraChange(CameraPosition position) {

}

@Override

            public void onCameraChangeFinish(CameraPosition position) {

//屏幕中心的Marker跳动

                startJumpAnimation();

if (isMoveFromUserCheckItem) {

isMoveFromUserCheckItem =false;

viewBinding.map.setEnabled(true);

}else {

lastPoint =new LatLonPoint(position.target.latitude, position.target.longitude);

currentPage =0;

doSearch();

}

}

});

startLocation();

}

//声明AMapLocationClient类对象

    private AMapLocationClientmLocationClient =null;

//声明定位回调监听器

    private AMapLocationListenermLocationListener =new AMapLocationListener() {

@Override

        public void onLocationChanged(AMapLocation aMapLocation) {

addMarkerInScreenCenter();

aMap.moveCamera(CameraUpdateFactory.newCameraPosition(new CameraPosition(new LatLng(aMapLocation.getLatitude(), aMapLocation.getLongitude()),18,30,30)));

lastPoint =new LatLonPoint(aMapLocation.getLatitude(), aMapLocation.getLongitude());

currentPage =0;

doSearch();

}

};

private void doSearch() {

searchQuery.setCityLimit(true);

searchQuery.setPageNum(currentPage);

poiSearch.setQuery(searchQuery);

poiSearch.setBound(new PoiSearchV2.SearchBound(lastPoint,5000));

// 根据经纬度执行异步查询

        poiSearch.searchPOIAsyn();

}

private PoiSearchV2poiSearch;

private PoiSearchV2.QuerysearchQuery;

int currentPage =0;

private LatLonPointlastPoint;

private PoiSearchV2.OnPoiSearchListeneronPoiSearchListener =new PoiSearchV2.OnPoiSearchListener() {

@SuppressLint("NewApi")

@Override

        public void onPoiSearched(PoiResultV2 poiResultV2,int code) {

if (code ==1000 && poiResultV2.getCount() >0) {

poiResultV2.getPois().stream().distinct();

if (currentPage ==0) {

currentPoi = poiResultV2.getPois().get(0);

poiAdapter.getData().clear();

}

viewBinding.refreshLayout.finishLoadMore(true);

viewBinding.loadingLayout.showContent();

poiAdapter.getData().addAll(poiResultV2.getPois());

poiAdapter.notifyDataSetChanged();

}else {

if (currentPage ==0) {

viewBinding.loadingLayout.showState(EmptyState.class);

}

viewBinding.refreshLayout.finishLoadMore(true);

}

}

@Override

        public void onPoiItemSearched(PoiItemV2 poiItemV2,int i) {

}

};

private void startLocation() {

//初始化定位

        try {

mLocationClient =new AMapLocationClient(getApplicationContext());

//设置定位回调监听

            AMapLocationClientOption mLocationOption =new AMapLocationClientOption();

mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);

mLocationOption.setOnceLocation(true);

mLocationOption.setOnceLocationLatest(true);

mLocationOption.setNeedAddress(true);

mLocationClient.setLocationOption(mLocationOption);

mLocationClient.setLocationListener(mLocationListener);

searchQuery =new PoiSearchV2.Query("","","");

searchQuery.setPageSize(20);

// 创建GeocodeSearch对象

            poiSearch =new PoiSearchV2(this,searchQuery);

// 设置解析监听器

            poiSearch.setOnPoiSearchListener(onPoiSearchListener);

requestPermissions();

}catch (Exception e) {

e.printStackTrace();

}

}

private void requestPermissions() {

if (XXPermissions.isGranted(getApplicationContext(), Permission.Group.LOCATION)) {

mLocationClient.startLocation();

}else {

XXPermissions.with(mContext)

.permission(Permission.Group.LOCATION)

.request(new OnPermissionCallback() {

@Override

                        public void onGranted(List permissions,boolean all) {

mLocationClient.startLocation();

}

@Override

                        public void onDenied(List permissions,boolean never) {

if (never) {

new XPopup.Builder(mContext)

.isDestroyOnDismiss(true)

.asConfirm("系统提示","请授予位置权限","否","是",new OnConfirmListener() {

@Override

                                            public void onConfirm() {

XXPermissions.startPermissionActivity(mContext, Permission.Group.LOCATION);

}

},null,false)

.show();

}else {

requestPermissions();

}

}

});

}

}

/**

* 屏幕中心marker 跳动

*/

    public void startJumpAnimation() {

if (screenMarker !=null) {

//根据屏幕距离计算需要移动的目标点

            final LatLng latLng =screenMarker.getPosition();

Point point =aMap.getProjection().toScreenLocation(latLng);

point.y -= SizeUtils.dp2px(80);

LatLng target =aMap.getProjection()

.fromScreenLocation(point);

//使用TranslateAnimation,填写一个需要移动的目标点

            Animation animation =new TranslateAnimation(target);

animation.setInterpolator(new Interpolator() {

@Override

                public float getInterpolation(float input) {

// 模拟重加速度的interpolator

                    if (input <=0.5) {

return (float) (0.5f -2 * (0.5 - input) * (0.5 - input));

}else {

return (float) (0.5f - Math.sqrt((input -0.5f) * (1.5f - input)));

}

}

});

//整个移动所需要的时间

            animation.setDuration(600);

//设置动画

            screenMarker.setAnimation(animation);

//开始动画

            screenMarker.startAnimation();

}else {

Log.e("amap","screenMarker is null");

}

}

/**

* 在屏幕中心添加一个Marker

*/

    private void addMarkerInScreenCenter() {

if (screenMarker !=null) {

screenMarker.destroy();

}

aMap.clear();

LatLng latLng =aMap.getCameraPosition().target;

Point screenPosition =aMap.getProjection().toScreenLocation(latLng);

screenMarker =aMap.addMarker(new MarkerOptions()

.anchor(0.5f,0.5f)

.icon(BitmapDescriptorFactory.fromResource(R.drawable.purple_pin)));

//设置Marker在屏幕上,不跟随地图移动

        screenMarker.setPositionByPixels(screenPosition.x, screenPosition.y);

}

@Override

    public void onClick(View v) {

if (NoDoubleClickUtils.isDoubleClick())return;

switch (v.getId()) {

case R.id.ivCloseBottom:

behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);

break;

case R.id.llSearchPlace:

viewBinding.llSearchPlace.setVisibility(View.GONE);

viewBinding.llSearchInput.setVisibility(View.VISIBLE);

behavior.setState(BottomSheetBehavior.STATE_EXPANDED);

ThreadUtils.runOnUiThreadDelayed(() -> {

//打卡键盘

                    KeyboardUtils.showSoftInput(viewBinding.etSearchContent);

},200);

break;

case R.id.tvSearchCancel:

KeyboardUtils.hideSoftInput(getWindow());

ThreadUtils.runOnUiThreadDelayed(() -> {

viewBinding.llSearchPlace.setVisibility(View.VISIBLE);

viewBinding.llSearchInput.setVisibility(View.GONE);

behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);

},200);

break;

case R.id.tvCancel:

setResult(RESULT_CANCELED);

finish();

break;

case R.id.rtvSure:

if (currentPoi ==null) {

showToast("请选择一个位置");

return;

}

setResult(RESULT_OK,new Intent().putExtra("extra",currentPoi.getLatLonPoint()));

finish();

break;

}

}

/**

* 设置一些amap的属性

*/

    private void setUpMap() {

// 如果要设置定位的默认状态,可以在此处进行设置

        myLocationStyle =new MyLocationStyle();

aMap.getUiSettings().setZoomControlsEnabled(false);

aMap.getUiSettings().setLogoBottomMargin(-100);

//aMap.getUiSettings().setMyLocationButtonEnabled(true);// 设置默认定位按钮是否显示

        aMap.setMyLocationEnabled(true);// 设置为true表示显示定位层并可触发定位,false表示隐藏定位层并不可触发定位,默认是false

        myLocationStyle.myLocationIcon(BitmapDescriptorFactory.fromResource(R.mipmap.gps_point));

myLocationStyle.anchor(0.5f,0.5f);

myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATE);

myLocationStyle.showMyLocation(true);

aMap.setMyLocationStyle(myLocationStyle);

}

@Override

    protected void onActivityResult(int requestCode,int resultCode,@Nullable Intent data) {

super.onActivityResult(requestCode, resultCode, data);

}

@Override

    protected void onResume() {

super.onResume();

viewBinding.map.onResume();

}

@Override

    protected void onPause() {

super.onPause();

viewBinding.map.onPause();

}

@Override

    protected void onSaveInstanceState(Bundle outState) {

super.onSaveInstanceState(outState);

viewBinding.map.onSaveInstanceState(outState);

}

@Override

    protected void onDestroy() {

KeyboardUtils.unregisterSoftInputChangedListener(getWindow());

super.onDestroy();

if (mLocationClient !=null) {

mLocationClient.stopLocation();

}

viewBinding.map.onDestroy();

}

}

你可能感兴趣的:(2022-09-01)