parent
24b12f9778
commit
d64411fa51
@ -0,0 +1,574 @@ |
|||||||
|
package com.gyf.csams.activity.model |
||||||
|
|
||||||
|
import android.app.Activity |
||||||
|
import android.app.Application |
||||||
|
import android.content.Context |
||||||
|
import android.graphics.BitmapFactory |
||||||
|
import android.view.inputmethod.InputMethodManager |
||||||
|
import androidx.lifecycle.AndroidViewModel |
||||||
|
import androidx.lifecycle.LiveData |
||||||
|
import androidx.lifecycle.MutableLiveData |
||||||
|
import com.baidu.mapapi.map.* |
||||||
|
import com.baidu.mapapi.model.LatLng |
||||||
|
import com.baidu.mapapi.model.LatLngBounds |
||||||
|
import com.baidu.mapapi.search.core.PoiInfo |
||||||
|
import com.baidu.mapapi.search.core.SearchResult |
||||||
|
import com.baidu.mapapi.search.geocode.* |
||||||
|
import com.baidu.mapapi.search.poi.* |
||||||
|
import com.baidu.mapapi.search.sug.SuggestionResult |
||||||
|
import com.baidu.mapapi.search.sug.SuggestionSearch |
||||||
|
import com.baidu.mapapi.search.sug.SuggestionSearchOption |
||||||
|
import com.gyf.csams.MyLocationListener |
||||||
|
import com.gyf.csams.NOT_IMPL_TIP |
||||||
|
import com.gyf.csams.R |
||||||
|
import com.gyf.lib.uikit.ScaffoldModel |
||||||
|
import com.gyf.lib.uikit.StringForm |
||||||
|
import com.gyf.lib.util.DATETIME_FORMAT |
||||||
|
import com.orhanobut.logger.Logger |
||||||
|
import kotlinx.coroutines.GlobalScope |
||||||
|
import kotlinx.coroutines.launch |
||||||
|
import java.util.* |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 申请活动数据状态管理 |
||||||
|
* |
||||||
|
* @constructor |
||||||
|
* |
||||||
|
* |
||||||
|
* @param application |
||||||
|
*/ |
||||||
|
class ApplyActViewModel(application: Application) : AndroidViewModel(application) { |
||||||
|
val activityName = |
||||||
|
StringForm(formDesc = application.getString(R.string.activity_name), textLength = 10) |
||||||
|
val activityTime = StringForm( |
||||||
|
formDesc = application.getString(R.string.activity_time), |
||||||
|
textLength = DATETIME_FORMAT.length |
||||||
|
) |
||||||
|
val activityAddress = |
||||||
|
StringForm(formDesc = application.getString(R.string.activity_address), textLength = 30) |
||||||
|
val activityDesc = |
||||||
|
StringForm(formDesc = application.getString(R.string.activity_desc), textLength = 50) |
||||||
|
val maxActivitySize = application.resources.getInteger(R.integer.activity_size) |
||||||
|
val activitySize = object : |
||||||
|
StringForm(formDesc = application.getString(R.string.activity_size), textLength = 2) { |
||||||
|
override fun onChange(value: String) { |
||||||
|
if (value.length > textLength) { |
||||||
|
formError.value = "${formDesc}不能超过最大长度$textLength" |
||||||
|
_formValue.value = value.slice(IntRange(0, textLength - 1)) |
||||||
|
} else if (value.matches(Regex("\\d+")) && value.toInt() !in 1..maxActivitySize) { |
||||||
|
Logger.i("活动人数:${value}不合法") |
||||||
|
formError.value = |
||||||
|
application.getString(R.string.activity_size_error, 1, maxActivitySize) |
||||||
|
} else { |
||||||
|
_formValue.value = value |
||||||
|
formError.value = "" |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
val city = |
||||||
|
object : StringForm(formDesc = application.getString(R.string.city), textLength = 4) { |
||||||
|
override val formPlaceholder = "" |
||||||
|
} |
||||||
|
|
||||||
|
// 默认逆地理编码半径范围 |
||||||
|
private val sDefaultRGCRadius = 500 |
||||||
|
|
||||||
|
private val mGeoCoder: GeoCoder = GeoCoder.newInstance().apply { |
||||||
|
setOnGetGeoCodeResultListener(object : OnGetGeoCoderResultListener { |
||||||
|
override fun onGetGeoCodeResult(p0: GeoCodeResult?) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
override fun onGetReverseGeoCodeResult(reverseGeoCodeResult: ReverseGeoCodeResult?) { |
||||||
|
reverseGeoCodeResult?.let { |
||||||
|
GlobalScope.launch { |
||||||
|
updateUI(it) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
lateinit var scaffoldModel: ScaffoldModel |
||||||
|
|
||||||
|
private val _mapView = MutableLiveData<MapView>() |
||||||
|
|
||||||
|
val mapView: LiveData<MapView> = _mapView |
||||||
|
|
||||||
|
/** |
||||||
|
* 更新UI |
||||||
|
* |
||||||
|
* @param reverseGeoCodeResult |
||||||
|
*/ |
||||||
|
private fun updateUI(reverseGeoCodeResult: ReverseGeoCodeResult) { |
||||||
|
Logger.i("逆编码更新地点信息") |
||||||
|
var poiInfo = reverseGeoCodeResult.poiList |
||||||
|
val curAddressPoiInfo = PoiInfo() |
||||||
|
curAddressPoiInfo.address = reverseGeoCodeResult.address |
||||||
|
curAddressPoiInfo.location = reverseGeoCodeResult.location |
||||||
|
if (null == poiInfo) { |
||||||
|
poiInfo = ArrayList(2) |
||||||
|
} |
||||||
|
poiInfo.add(0, curAddressPoiInfo) |
||||||
|
|
||||||
|
_poInfo.postValue(poiInfo) |
||||||
|
_selectPoi.postValue(null) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
fun searchPoiInCity() { |
||||||
|
if (city.formValue.value?.isEmpty() == true || |
||||||
|
address.formValue.value?.isEmpty() == true |
||||||
|
) { |
||||||
|
Logger.i("city=${city.formValue.value},keyword=${address.formValue.value}") |
||||||
|
return |
||||||
|
} else { |
||||||
|
Logger.i("在${city.formValue.value}市内找${address.formValue.value}") |
||||||
|
mPoiSearch.searchInCity( |
||||||
|
PoiCitySearchOption() |
||||||
|
.city(city.formValue.value) |
||||||
|
.keyword(address.formValue.value) |
||||||
|
.pageNum(0) // 分页编号 |
||||||
|
.cityLimit(true) |
||||||
|
.scope(1) |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 在地图上定位poi |
||||||
|
* |
||||||
|
* @param suggestInfo |
||||||
|
*/ |
||||||
|
fun locateSuggestPoi(suggestInfo: SuggestionResult.SuggestionInfo) { |
||||||
|
// _sugResult.value?.clear() |
||||||
|
_sugResult.value = mutableListOf() |
||||||
|
_mapView.value?.apply { |
||||||
|
val latLng = suggestInfo.getPt() |
||||||
|
|
||||||
|
_mStatusChangeByItemClick.value = false |
||||||
|
// 将地图平移到 latLng 位置 |
||||||
|
val mapStatusUpdate = MapStatusUpdateFactory.newLatLng(latLng) |
||||||
|
map.setMapStatus(mapStatusUpdate) |
||||||
|
|
||||||
|
// 清除之前的 |
||||||
|
// clearData() |
||||||
|
|
||||||
|
// 显示当前的 |
||||||
|
// if (!showSuggestMarker(latLng)) { |
||||||
|
// searchPoiInCity() |
||||||
|
// } |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 显示定位点 |
||||||
|
* |
||||||
|
* @param latLng |
||||||
|
*/ |
||||||
|
private fun showSuggestMarker(latLng: LatLng?): Boolean { |
||||||
|
if (null == latLng) { |
||||||
|
return false |
||||||
|
} |
||||||
|
_mapView.value?.apply { |
||||||
|
val markerOptions = MarkerOptions() |
||||||
|
.position(latLng) |
||||||
|
.icon(mBitmapDescWaterDrop) |
||||||
|
.scaleX(1.5f) |
||||||
|
.scaleY(1.5f) |
||||||
|
.clickable(true) |
||||||
|
map.addOverlay(markerOptions) |
||||||
|
return true |
||||||
|
} |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
val mSuggestionSearch: SuggestionSearch = SuggestionSearch.newInstance().apply { |
||||||
|
setOnGetSuggestionResultListener { it -> |
||||||
|
if (it == null |
||||||
|
|| it.error == SearchResult.ERRORNO.RESULT_NOT_FOUND |
||||||
|
) { |
||||||
|
scaffoldModel.update( |
||||||
|
message = application.getString( |
||||||
|
R.string.search_null |
||||||
|
) |
||||||
|
) |
||||||
|
Logger.i("sug检索不到地点") |
||||||
|
} else { |
||||||
|
it.allSuggestions?.let { |
||||||
|
Logger.i("sug检索到${it.size}个地点") |
||||||
|
_sugResult.value = it |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private val mPoiSearch: PoiSearch = PoiSearch.newInstance().apply { |
||||||
|
setOnGetPoiSearchResultListener( |
||||||
|
object : OnGetPoiSearchResultListener { |
||||||
|
override fun onGetPoiResult( |
||||||
|
poiResult: PoiResult? |
||||||
|
) { |
||||||
|
if (poiResult == null || poiResult.error == SearchResult.ERRORNO.RESULT_NOT_FOUND) { |
||||||
|
scaffoldModel.update( |
||||||
|
message = application.getString( |
||||||
|
R.string.search_null |
||||||
|
) |
||||||
|
) |
||||||
|
Logger.i("Poi检索不到地点") |
||||||
|
} else { |
||||||
|
poiResult.allPoi?.let { |
||||||
|
Logger.i("Poi检索到${it.size}个地点") |
||||||
|
setPoiResult(it) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Suppress("DEPRECATION") |
||||||
|
override fun onGetPoiDetailResult(p0: PoiDetailResult?) { |
||||||
|
Logger.i("$p0") |
||||||
|
} |
||||||
|
|
||||||
|
override fun onGetPoiDetailResult(p0: PoiDetailSearchResult?) { |
||||||
|
Logger.i("$p0") |
||||||
|
} |
||||||
|
|
||||||
|
override fun onGetPoiIndoorResult(p0: PoiIndoorResult?) { |
||||||
|
Logger.i("$p0") |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
private val mBitmapDescWaterDrop = BitmapDescriptorFactory.fromBitmap( |
||||||
|
BitmapFactory.decodeResource( |
||||||
|
application.resources, |
||||||
|
R.drawable.icon_binding_point |
||||||
|
) |
||||||
|
) |
||||||
|
|
||||||
|
|
||||||
|
private val mMarkerPoiInfo = mutableMapOf<Marker, PoiInfo>() |
||||||
|
|
||||||
|
private var mPreSelectMarker: Marker? = null |
||||||
|
|
||||||
|
private fun showPoiMarker(poiInfo: PoiInfo?, i: Int) { |
||||||
|
poiInfo?.apply { |
||||||
|
_mapView.value?.apply { |
||||||
|
val markerOptions = MarkerOptions() |
||||||
|
.position(location) |
||||||
|
.icon(mBitmapDescWaterDrop) |
||||||
|
|
||||||
|
// 第一个poi放大显示 |
||||||
|
// if (0 == i) { |
||||||
|
// val infoWindow: InfoWindow = getPoiInfoWindow(poiInfo) |
||||||
|
// markerOptions.scaleX(1.5f).scaleY(1.5f).infoWindow(infoWindow) |
||||||
|
// } |
||||||
|
val marker = map.addOverlay(markerOptions) as Marker |
||||||
|
mMarkerPoiInfo[marker] = poiInfo |
||||||
|
if (0 == i) { |
||||||
|
mPreSelectMarker = marker |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
private fun setPoiResult(poiInfos: List<PoiInfo>?) { |
||||||
|
_mapView.value?.apply { |
||||||
|
if (null == poiInfos || poiInfos.isEmpty()) { |
||||||
|
return |
||||||
|
} |
||||||
|
clearData() |
||||||
|
|
||||||
|
// 将地图平移到 latLng 位置 |
||||||
|
val latLng = poiInfos[0].getLocation() |
||||||
|
map.setMapStatus(mapStatus(latLng)) |
||||||
|
val itr: Iterator<*> = poiInfos.iterator() |
||||||
|
val latLngs: MutableList<LatLng> = ArrayList() |
||||||
|
var poiInfo: PoiInfo? |
||||||
|
var i = 0 |
||||||
|
while (itr.hasNext()) { |
||||||
|
poiInfo = itr.next() as PoiInfo? |
||||||
|
if (null == poiInfo) { |
||||||
|
continue |
||||||
|
} |
||||||
|
showPoiMarker(poiInfo, i) |
||||||
|
latLngs.add(poiInfo.getLocation()) |
||||||
|
i++ |
||||||
|
} |
||||||
|
setBounds(latLngs) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 最佳视野内显示所有点标记 |
||||||
|
*/ |
||||||
|
private fun setBounds(latLngs: List<LatLng>?) { |
||||||
|
if (null == latLngs || latLngs.isEmpty()) { |
||||||
|
return |
||||||
|
} |
||||||
|
val horizontalPadding = 80 |
||||||
|
val verticalPaddingBottom = 400 |
||||||
|
|
||||||
|
// 构造地理范围对象 |
||||||
|
val builder = LatLngBounds.Builder() |
||||||
|
// 让该地理范围包含一组地理位置坐标 |
||||||
|
builder.include(latLngs) |
||||||
|
|
||||||
|
_mapView.value?.apply { |
||||||
|
// 设置显示在指定相对于MapView的padding中的地图地理范围 |
||||||
|
val mapStatusUpdate = MapStatusUpdateFactory.newLatLngBounds( |
||||||
|
builder.build(), |
||||||
|
horizontalPadding, |
||||||
|
verticalPaddingBottom, |
||||||
|
horizontalPadding, |
||||||
|
verticalPaddingBottom |
||||||
|
) |
||||||
|
// 更新地图 |
||||||
|
map.setMapStatus(mapStatusUpdate) |
||||||
|
// 设置地图上控件与地图边界的距离,包含比例尺、缩放控件、logo、指南针的位置 |
||||||
|
map.setViewPadding( |
||||||
|
0, |
||||||
|
0, |
||||||
|
0, |
||||||
|
verticalPaddingBottom |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
private fun clearData() { |
||||||
|
Logger.i("清空地图数据") |
||||||
|
_mapView.value?.apply { |
||||||
|
map.clear() |
||||||
|
mMarkerPoiInfo.clear() |
||||||
|
mPreSelectMarker = null |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 创建地图中心点marker |
||||||
|
*/ |
||||||
|
private fun createCenterMarker(mBaiduMap: BaiduMap, mCenter: LatLng) { |
||||||
|
Logger.i("标记坐标${mCenter}为地图中心点") |
||||||
|
val projection: Projection = mBaiduMap.projection ?: return |
||||||
|
val point = projection.toScreenLocation(mCenter) |
||||||
|
val markerOptions = MarkerOptions() |
||||||
|
.position(mCenter) |
||||||
|
.icon(mBitmapDescWaterDrop) |
||||||
|
.flat(false) |
||||||
|
.fixedScreenPosition(point) |
||||||
|
marker = markerOptions |
||||||
|
mBaiduMap.addOverlay(markerOptions) |
||||||
|
} |
||||||
|
|
||||||
|
fun isLatlngEqual(latLng0: LatLng, latLng1: LatLng): Boolean { |
||||||
|
return (latLng0.latitude == latLng1.latitude |
||||||
|
&& latLng0.longitude == latLng1.longitude) |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 逆地理编码请求 |
||||||
|
* |
||||||
|
* @param latLng |
||||||
|
*/ |
||||||
|
private fun reverseRequest(latLng: LatLng?) { |
||||||
|
Logger.i("逆地理编码请求") |
||||||
|
latLng?.let { |
||||||
|
val reverseGeoCodeOption = ReverseGeoCodeOption().location(it) |
||||||
|
.newVersion(1) // 建议请求新版数据 |
||||||
|
.radius(sDefaultRGCRadius) |
||||||
|
mGeoCoder.reverseGeoCode(reverseGeoCodeOption) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
private fun mapStatus(mCenter: LatLng): MapStatusUpdate { |
||||||
|
return MapStatusUpdateFactory.newLatLngZoom( |
||||||
|
mCenter, |
||||||
|
16f |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
fun updateMapStatus(mapStatus: MapStatus?) { |
||||||
|
mapStatus?.target?.let { |
||||||
|
// 如果是点击poi item导致的地图状态更新,则不用做后面的逆地理请求, |
||||||
|
if (_mStatusChangeByItemClick.value == true) { |
||||||
|
if (!isLatlngEqual( |
||||||
|
mCenter, |
||||||
|
it |
||||||
|
) |
||||||
|
) { |
||||||
|
mCenter = it |
||||||
|
} |
||||||
|
_mStatusChangeByItemClick.postValue(false) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
if (!isLatlngEqual(mCenter, it)) { |
||||||
|
mCenter = it |
||||||
|
reverseRequest(mCenter) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private lateinit var mCenter: LatLng |
||||||
|
|
||||||
|
private lateinit var marker: MarkerOptions |
||||||
|
|
||||||
|
fun hideKeyBoard(activity: Activity?) { |
||||||
|
Logger.i("隐藏软键盘") |
||||||
|
activity?.apply { |
||||||
|
val imm = getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager |
||||||
|
window.peekDecorView()?.let { |
||||||
|
imm.hideSoftInputFromWindow(it.windowToken, 0) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun createMapView(context: Context): MapView { |
||||||
|
val mapView = MapView(context).apply { |
||||||
|
|
||||||
|
MyLocationListener.location.value?.let { _ -> |
||||||
|
// 设置初始中心点为用户附近 |
||||||
|
// mCenter = LatLng(it.latitude, it.longitude) |
||||||
|
mCenter = LatLng(39.963175, 116.400244) |
||||||
|
|
||||||
|
map.setMapStatus(mapStatus(mCenter = mCenter)) |
||||||
|
|
||||||
|
map.setOnMapTouchListener { |
||||||
|
hideKeyBoard(context as Activity) |
||||||
|
} |
||||||
|
|
||||||
|
map.setOnMarkerClickListener { |
||||||
|
scaffoldModel.update(message = "确定选中此地址", actionLabel = "确定") { |
||||||
|
val poi = (_selectPoi.value ?: poiInfo.value?.get(0)) |
||||||
|
val address = poi?.address ?: poi?.name ?: "地址获取失败,请再尝试一次" |
||||||
|
if (address.isNotEmpty()) { |
||||||
|
activityAddress.onChange(address) |
||||||
|
} else { |
||||||
|
Logger.w("地址获取失败") |
||||||
|
} |
||||||
|
|
||||||
|
closeMap() |
||||||
|
} |
||||||
|
true |
||||||
|
} |
||||||
|
|
||||||
|
map.removeMarkerClickListener { |
||||||
|
Logger.i("删除标记") |
||||||
|
true |
||||||
|
} |
||||||
|
|
||||||
|
map.setOnMapLoadedCallback { |
||||||
|
Logger.i("地图加载完成!") |
||||||
|
// createCenterMarker(map, mCenter) |
||||||
|
map.addOverlay(MarkerOptions().position(mCenter).icon(mBitmapDescWaterDrop)) |
||||||
|
reverseRequest(mCenter) |
||||||
|
} |
||||||
|
|
||||||
|
map.setOnMapStatusChangeListener(object : |
||||||
|
BaiduMap.OnMapStatusChangeListener { |
||||||
|
override fun onMapStatusChangeStart(p0: MapStatus?) { |
||||||
|
Logger.i("状态开始改变:$p0") |
||||||
|
} |
||||||
|
|
||||||
|
override fun onMapStatusChangeStart( |
||||||
|
p0: MapStatus?, |
||||||
|
p1: Int |
||||||
|
) { |
||||||
|
Logger.i("状态改变:$p0,p1=$p1") |
||||||
|
} |
||||||
|
|
||||||
|
override fun onMapStatusChange(mapStatus: MapStatus?) { |
||||||
|
Logger.i("状态改变:$mapStatus,_mStatusChangeByItemClick.value=${_mStatusChangeByItemClick.value}") |
||||||
|
updateMapStatus(mapStatus = mapStatus) |
||||||
|
map.clear() |
||||||
|
map.addOverlay(MarkerOptions().position(mCenter).icon(mBitmapDescWaterDrop)) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onMapStatusChangeFinish( |
||||||
|
mapStatus: MapStatus? |
||||||
|
) { |
||||||
|
Logger.i("状态改变完成:$mapStatus") |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
_mapView.value = mapView |
||||||
|
return mapView |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
val address = object : StringForm(formDesc = "", textLength = 5) { |
||||||
|
override val formPlaceholder = "" |
||||||
|
|
||||||
|
override fun onChange(value: String) { |
||||||
|
super.onChange(value) |
||||||
|
Logger.i("Sug检索,城市:${city.formValue.value},地点关键词:${value}") |
||||||
|
// _sugResult.value?.clear() |
||||||
|
_sugResult.value = mutableListOf() |
||||||
|
mSuggestionSearch.requestSuggestion( |
||||||
|
SuggestionSearchOption() |
||||||
|
.city(city.formValue.value) |
||||||
|
.keyword(value) |
||||||
|
.citylimit(true) |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private val _showMap = MutableLiveData<Boolean>() |
||||||
|
val showMap: LiveData<Boolean> = _showMap |
||||||
|
|
||||||
|
private val _mStatusChangeByItemClick = MutableLiveData(false) |
||||||
|
|
||||||
|
private val _poInfo = MutableLiveData<List<PoiInfo>>() |
||||||
|
val poiInfo: LiveData<List<PoiInfo>> = _poInfo |
||||||
|
|
||||||
|
private val _selectPoi = MutableLiveData<PoiInfo?>() |
||||||
|
val selectPoi: LiveData<PoiInfo?> = _selectPoi |
||||||
|
|
||||||
|
private val _sugResult = MutableLiveData<MutableList<SuggestionResult.SuggestionInfo>>() |
||||||
|
val sugResult: LiveData<MutableList<SuggestionResult.SuggestionInfo>> = _sugResult |
||||||
|
|
||||||
|
fun updateSelectPoi(poiInfo: PoiInfo?) { |
||||||
|
_selectPoi.value = poiInfo |
||||||
|
} |
||||||
|
|
||||||
|
fun updateStatusChangeByItemClick(flag: Boolean) { |
||||||
|
_mStatusChangeByItemClick.value = flag |
||||||
|
} |
||||||
|
|
||||||
|
fun openMap() { |
||||||
|
_showMap.value = true |
||||||
|
} |
||||||
|
|
||||||
|
private fun closeMap() { |
||||||
|
_showMap.value = false |
||||||
|
|
||||||
|
clearData() |
||||||
|
} |
||||||
|
|
||||||
|
private fun destroy() { |
||||||
|
mPoiSearch.destroy() |
||||||
|
|
||||||
|
mSuggestionSearch.destroy() |
||||||
|
|
||||||
|
mapView.value?.onDestroy() |
||||||
|
|
||||||
|
mBitmapDescWaterDrop?.recycle() |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* TODO 提交申请 |
||||||
|
* |
||||||
|
*/ |
||||||
|
fun apply(callback: (message: String) -> Unit) { |
||||||
|
callback(NOT_IMPL_TIP) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,495 @@ |
|||||||
|
package com.gyf.csams.activity.ui |
||||||
|
|
||||||
|
import android.Manifest |
||||||
|
import android.content.pm.PackageManager |
||||||
|
import android.os.Bundle |
||||||
|
import androidx.activity.compose.rememberLauncherForActivityResult |
||||||
|
import androidx.activity.compose.setContent |
||||||
|
import androidx.activity.result.contract.ActivityResultContracts |
||||||
|
import androidx.appcompat.app.AppCompatActivity |
||||||
|
import androidx.compose.foundation.clickable |
||||||
|
import androidx.compose.foundation.layout.* |
||||||
|
import androidx.compose.foundation.lazy.LazyColumn |
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState |
||||||
|
import androidx.compose.foundation.text.KeyboardOptions |
||||||
|
import androidx.compose.material.* |
||||||
|
import androidx.compose.runtime.Composable |
||||||
|
import androidx.compose.runtime.LaunchedEffect |
||||||
|
import androidx.compose.runtime.getValue |
||||||
|
import androidx.compose.runtime.livedata.observeAsState |
||||||
|
import androidx.compose.runtime.rememberCoroutineScope |
||||||
|
import androidx.compose.ui.Alignment |
||||||
|
import androidx.compose.ui.Modifier |
||||||
|
import androidx.compose.ui.platform.LocalContext |
||||||
|
import androidx.compose.ui.res.painterResource |
||||||
|
import androidx.compose.ui.res.stringResource |
||||||
|
import androidx.compose.ui.text.input.KeyboardType |
||||||
|
import androidx.compose.ui.unit.dp |
||||||
|
import androidx.compose.ui.viewinterop.AndroidView |
||||||
|
import androidx.core.content.ContextCompat |
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel |
||||||
|
import com.baidu.location.LocationClient |
||||||
|
import com.baidu.location.LocationClientOption |
||||||
|
import com.baidu.mapapi.map.MapStatusUpdateFactory |
||||||
|
import com.baidu.mapapi.search.sug.SuggestionResult |
||||||
|
import com.google.android.material.datepicker.MaterialDatePicker |
||||||
|
import com.gyf.csams.MainApplication |
||||||
|
import com.gyf.csams.MyLocationListener |
||||||
|
import com.gyf.csams.R |
||||||
|
import com.gyf.csams.activity.model.ApplyActViewModel |
||||||
|
import com.gyf.csams.uikit.Background |
||||||
|
import com.gyf.csams.uikit.BackgroundImage |
||||||
|
import com.gyf.csams.uikit.DescCard |
||||||
|
import com.gyf.lib.uikit.* |
||||||
|
import com.gyf.lib.util.BottomButton |
||||||
|
import com.gyf.lib.util.format |
||||||
|
import com.orhanobut.logger.Logger |
||||||
|
import kotlinx.coroutines.launch |
||||||
|
import java.util.* |
||||||
|
|
||||||
|
|
||||||
|
val location_permissions = arrayOf( |
||||||
|
Manifest.permission.ACCESS_COARSE_LOCATION, |
||||||
|
Manifest.permission.ACCESS_FINE_LOCATION, |
||||||
|
Manifest.permission.ACCESS_WIFI_STATE, |
||||||
|
Manifest.permission.ACCESS_NETWORK_STATE, |
||||||
|
Manifest.permission.CHANGE_WIFI_STATE |
||||||
|
) |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 申请活动 |
||||||
|
* |
||||||
|
*/ |
||||||
|
class ApplyActActivity : AppCompatActivity() { |
||||||
|
|
||||||
|
private lateinit var mLocationClient: LocationClient |
||||||
|
|
||||||
|
override fun onStop() { |
||||||
|
super.onStop() |
||||||
|
mLocationClient.stop() |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) { |
||||||
|
super.onCreate(savedInstanceState) |
||||||
|
|
||||||
|
mLocationClient = (application as MainApplication).mLocationClient |
||||||
|
|
||||||
|
setContent { |
||||||
|
Body { scaffoldState -> |
||||||
|
MainColumnFrame(background = { Background(image = BackgroundImage.ApplyActivity) }) { |
||||||
|
val model: ApplyActViewModel = viewModel() |
||||||
|
val scaffoldModel: ScaffoldModel = viewModel() |
||||||
|
model.scaffoldModel = scaffoldModel |
||||||
|
val showMap by model.showMap.observeAsState(false) |
||||||
|
val location by MyLocationListener.location.observeAsState() |
||||||
|
|
||||||
|
when { |
||||||
|
showMap && location != null -> { |
||||||
|
Box { |
||||||
|
val sugResult by model.sugResult.observeAsState() |
||||||
|
|
||||||
|
Column(modifier = Modifier.fillMaxSize()) { |
||||||
|
AndroidView(modifier = Modifier |
||||||
|
.weight(0.6F) |
||||||
|
.fillMaxWidth(), |
||||||
|
factory = { context -> model.createMapView(context) }) |
||||||
|
|
||||||
|
if (sugResult == null || sugResult?.size == 0) { |
||||||
|
PoiItem( |
||||||
|
modifier = Modifier |
||||||
|
.weight(0.4F) |
||||||
|
.fillMaxWidth() |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
Column(modifier = Modifier.fillMaxSize()) { |
||||||
|
|
||||||
|
Search( |
||||||
|
modifier = Modifier |
||||||
|
.weight(0.2F) |
||||||
|
.fillMaxWidth() |
||||||
|
) |
||||||
|
|
||||||
|
sugResult.let { |
||||||
|
if (it.isNullOrEmpty()) { |
||||||
|
Spacer( |
||||||
|
modifier = Modifier |
||||||
|
.weight(0.8F) |
||||||
|
.fillMaxWidth() |
||||||
|
) |
||||||
|
} else { |
||||||
|
SuggestItem( |
||||||
|
modifier = Modifier |
||||||
|
.weight(0.8F) |
||||||
|
.fillMaxWidth(), |
||||||
|
sugResult = it |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
else -> { |
||||||
|
Title(modifier = Modifier.weight(0.2F)) |
||||||
|
Row( |
||||||
|
Modifier |
||||||
|
.weight(0.1F) |
||||||
|
.fillMaxWidth(), horizontalArrangement = Arrangement.Center |
||||||
|
) { |
||||||
|
BaseTextField(form = model.activityName) |
||||||
|
} |
||||||
|
Row( |
||||||
|
Modifier |
||||||
|
.weight(0.1F) |
||||||
|
.fillMaxWidth(), |
||||||
|
horizontalArrangement = Arrangement.Center |
||||||
|
) { |
||||||
|
ActivityTime() |
||||||
|
} |
||||||
|
ActivityLocation( |
||||||
|
modifier = Modifier |
||||||
|
.weight(0.15F) |
||||||
|
.fillMaxWidth() |
||||||
|
) |
||||||
|
DescCard( |
||||||
|
modifier = Modifier.weight(0.3F), |
||||||
|
stringForm = model.activityDesc |
||||||
|
) |
||||||
|
val error by model.activitySize.formError.observeAsState() |
||||||
|
if (error?.isNotEmpty() == true) { |
||||||
|
scaffoldModel.update(message = error) |
||||||
|
} |
||||||
|
Row( |
||||||
|
Modifier |
||||||
|
.weight(0.1F) |
||||||
|
.fillMaxWidth(), horizontalArrangement = Arrangement.Center |
||||||
|
) { |
||||||
|
BaseTextField( |
||||||
|
form = model.activitySize, |
||||||
|
keyboardOptions = |
||||||
|
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number) |
||||||
|
) |
||||||
|
} |
||||||
|
Spacer(modifier = Modifier.weight(0.05F)) |
||||||
|
BottomButton(modifier = Modifier.fillMaxWidth()) { |
||||||
|
model.apply { scaffoldModel.update(message = it) } |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
ShowSnackbar(scaffoldState = scaffoldState) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
private fun SelectIcon(modifier: Modifier = Modifier) { |
||||||
|
IconButton(modifier = modifier, onClick = { |
||||||
|
|
||||||
|
}) { |
||||||
|
Icon( |
||||||
|
painter = painterResource(id = R.drawable.ic_seleted), |
||||||
|
contentDescription = null |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
private fun Search( |
||||||
|
modifier: Modifier = Modifier, |
||||||
|
model: ApplyActViewModel = viewModel() |
||||||
|
) { |
||||||
|
Column( |
||||||
|
modifier = modifier |
||||||
|
.fillMaxWidth() |
||||||
|
// .border(width = 1.dp, color = MaterialTheme.colors.onBackground) |
||||||
|
, |
||||||
|
verticalArrangement = Arrangement.SpaceAround |
||||||
|
) { |
||||||
|
Card(backgroundColor = MaterialTheme.colors.background) { |
||||||
|
Row(modifier = Modifier.fillMaxWidth()) { |
||||||
|
BaseTextField(modifier = Modifier.weight(0.5F), |
||||||
|
form = model.city, leadingIcon = { |
||||||
|
Text( |
||||||
|
text = stringResource(id = R.string.at), |
||||||
|
style = MaterialTheme.typography.subtitle2 |
||||||
|
) |
||||||
|
}, trailingIcon = { |
||||||
|
Text( |
||||||
|
text = stringResource(id = R.string.incity), |
||||||
|
style = MaterialTheme.typography.subtitle2 |
||||||
|
) |
||||||
|
}, textStyle = MaterialTheme.typography.subtitle2 |
||||||
|
) |
||||||
|
BaseTextField(modifier = Modifier.weight(0.5F), form = model.address) |
||||||
|
} |
||||||
|
} |
||||||
|
// Row( |
||||||
|
// modifier = Modifier.fillMaxWidth(), |
||||||
|
// horizontalArrangement = Arrangement.Center |
||||||
|
// ) { |
||||||
|
// OutlinedButton(onClick = { |
||||||
|
// model.searchPoiInCity() |
||||||
|
// }) { |
||||||
|
// Text(text = stringResource(id = R.string.search_btn)) |
||||||
|
// } |
||||||
|
// } |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
private fun SuggestItem( |
||||||
|
modifier: Modifier = Modifier, |
||||||
|
model: ApplyActViewModel = viewModel(), |
||||||
|
sugResult: List<SuggestionResult.SuggestionInfo> |
||||||
|
) { |
||||||
|
val state = rememberLazyListState() |
||||||
|
|
||||||
|
LazyColumn(modifier = modifier, state = state) { |
||||||
|
sugResult.forEach { |
||||||
|
item { |
||||||
|
Card(backgroundColor = MaterialTheme.colors.background) { |
||||||
|
Column( |
||||||
|
modifier = Modifier |
||||||
|
.fillMaxWidth() |
||||||
|
.clickable(onClick = { |
||||||
|
model.hideKeyBoard(this@ApplyActActivity) |
||||||
|
model.locateSuggestPoi(suggestInfo = it) |
||||||
|
}) |
||||||
|
) { |
||||||
|
Text(text = it.key) |
||||||
|
it.address?.let { |
||||||
|
Text(text = it) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
Divider(color = MaterialTheme.colors.onBackground) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
private fun PoiItem( |
||||||
|
modifier: Modifier = Modifier, |
||||||
|
model: ApplyActViewModel = viewModel() |
||||||
|
) { |
||||||
|
val state = rememberLazyListState() |
||||||
|
val poiInfo by model.poiInfo.observeAsState() |
||||||
|
val selectPoi by model.selectPoi.observeAsState() |
||||||
|
val mapView by model.mapView.observeAsState() |
||||||
|
LazyColumn(modifier = modifier, state = state) { |
||||||
|
poiInfo?.withIndex()?.forEach { it -> |
||||||
|
if (it.value.name?.isNotEmpty() == true || it.value.address?.isNotEmpty() == true) { |
||||||
|
item { |
||||||
|
Card(modifier = Modifier.clickable(onClick = { |
||||||
|
model.updateStatusChangeByItemClick(true) |
||||||
|
val mapStatusUpdate = |
||||||
|
MapStatusUpdateFactory.newLatLng(it.value.getLocation()) |
||||||
|
mapView?.map?.setMapStatus(mapStatusUpdate) |
||||||
|
model.updateSelectPoi(it.value) |
||||||
|
}), backgroundColor = MaterialTheme.colors.background) { |
||||||
|
Row( |
||||||
|
modifier = Modifier |
||||||
|
.fillMaxWidth(), |
||||||
|
horizontalArrangement = Arrangement.SpaceBetween, |
||||||
|
verticalAlignment = Alignment.CenterVertically |
||||||
|
) { |
||||||
|
if (it.index == 0) { |
||||||
|
Text( |
||||||
|
text = "【${it.value.address}】", |
||||||
|
color = MaterialTheme.colors.primary, |
||||||
|
modifier = Modifier.weight(0.8F) |
||||||
|
) |
||||||
|
if (selectPoi == null || selectPoi == it.value) { |
||||||
|
SelectIcon(modifier = Modifier.weight(0.2F)) |
||||||
|
} else { |
||||||
|
Spacer(modifier = Modifier.weight(0.2F)) |
||||||
|
} |
||||||
|
} else { |
||||||
|
Column( |
||||||
|
modifier = Modifier.weight(0.8F) |
||||||
|
) { |
||||||
|
Text( |
||||||
|
text = it.value.name, |
||||||
|
color = MaterialTheme.colors.primary |
||||||
|
) |
||||||
|
it.value.address?.let { |
||||||
|
Text(text = it, color = MaterialTheme.colors.secondary) |
||||||
|
} |
||||||
|
} |
||||||
|
if (selectPoi == it.value) { |
||||||
|
SelectIcon(modifier = Modifier.weight(0.2F)) |
||||||
|
} else { |
||||||
|
Spacer(modifier = Modifier.weight(0.2F)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
Spacer(modifier = Modifier.height(10.dp)) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
poiInfo?.apply { |
||||||
|
LaunchedEffect(poiInfo) { |
||||||
|
launch { |
||||||
|
if (size > 0) { |
||||||
|
state.animateScrollToItem(0) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Composable |
||||||
|
private fun Title(modifier: Modifier = Modifier) { |
||||||
|
Box(modifier = modifier.fillMaxWidth(), contentAlignment = Alignment.Center) { |
||||||
|
Text( |
||||||
|
text = stringResource(id = R.string.activity_application), |
||||||
|
style = MaterialTheme.typography.h4 |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 活动时间 |
||||||
|
* |
||||||
|
* @param modifier |
||||||
|
* @param model |
||||||
|
*/ |
||||||
|
@Composable |
||||||
|
private fun ActivityTime( |
||||||
|
modifier: Modifier = Modifier, |
||||||
|
model: ApplyActViewModel = viewModel() |
||||||
|
) { |
||||||
|
val context = LocalContext.current as ApplyActActivity |
||||||
|
val scope = rememberCoroutineScope() |
||||||
|
BaseTextField( |
||||||
|
modifier = modifier, |
||||||
|
form = model.activityTime, readOnly = true |
||||||
|
) { |
||||||
|
IconButton(onClick = { |
||||||
|
scope.launch { |
||||||
|
val picker = MaterialDatePicker |
||||||
|
.Builder |
||||||
|
.datePicker() |
||||||
|
.setSelection(Date().time) |
||||||
|
.build() |
||||||
|
picker.show(context.supportFragmentManager, picker.toString()) |
||||||
|
picker.addOnPositiveButtonClickListener { |
||||||
|
model.activityTime.onChange(Date(it).format()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
}) { |
||||||
|
Icon( |
||||||
|
painter = painterResource(id = R.drawable.ic_date), |
||||||
|
contentDescription = null |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun requestLocation() { |
||||||
|
//如果想获取地址信息,需在配置LocationClientOption类时做相应的设置 |
||||||
|
val option = LocationClientOption() |
||||||
|
option.locationMode = LocationClientOption.LocationMode.Hight_Accuracy |
||||||
|
//可选,是否需要地址信息,默认为不需要,即参数为false |
||||||
|
//如果开发者需要获得当前点的地址信息,此处必须为true |
||||||
|
option.setIsNeedAddress(true) |
||||||
|
//可选,设置是否需要最新版本的地址信息。默认需要,即参数为true |
||||||
|
option.setNeedNewVersionRgc(true) |
||||||
|
|
||||||
|
|
||||||
|
mLocationClient.locOption = option |
||||||
|
Logger.i("开始定位") |
||||||
|
mLocationClient.start() |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private fun checkSelfPermissions(): Int { |
||||||
|
location_permissions.forEach { |
||||||
|
when (val v = ContextCompat.checkSelfPermission(this, it)) { |
||||||
|
PackageManager.PERMISSION_GRANTED -> Logger.i("权限${it}已放通") |
||||||
|
else -> { |
||||||
|
Logger.i("权限${it}检查状态:$v") |
||||||
|
return v |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
Logger.i("已授权") |
||||||
|
return PackageManager.PERMISSION_GRANTED |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 活动地点 |
||||||
|
* |
||||||
|
* @param modifier |
||||||
|
* @param model |
||||||
|
*/ |
||||||
|
@Composable |
||||||
|
private fun ActivityLocation( |
||||||
|
modifier: Modifier = Modifier, |
||||||
|
model: ApplyActViewModel = viewModel(), |
||||||
|
scaffoldModel: ScaffoldModel = viewModel() |
||||||
|
) { |
||||||
|
val launcher = rememberLauncherForActivityResult( |
||||||
|
ActivityResultContracts.RequestMultiplePermissions() |
||||||
|
) { it -> |
||||||
|
var isGranted = true |
||||||
|
it.entries.forEach { |
||||||
|
isGranted = isGranted && it.value |
||||||
|
Logger.i("权限:${it.key}授权结果:${it.value}") |
||||||
|
} |
||||||
|
|
||||||
|
if (isGranted) { |
||||||
|
// Permission Accepted: Do something |
||||||
|
requestLocation() |
||||||
|
model.openMap() |
||||||
|
Logger.i("准备打开地图") |
||||||
|
} else { |
||||||
|
// Permission Denied: Do something |
||||||
|
scaffoldModel.update(message = getString(R.string.denined_location_permission)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
BaseTextField(modifier = modifier, form = model.activityAddress, readOnly = true) { |
||||||
|
IconButton(onClick = { |
||||||
|
when (PackageManager.PERMISSION_GRANTED) { |
||||||
|
checkSelfPermissions() -> { |
||||||
|
// Some works that require permission |
||||||
|
requestLocation() |
||||||
|
model.openMap() |
||||||
|
Logger.i("准备打开地图") |
||||||
|
} |
||||||
|
else -> { |
||||||
|
// Asking for permission |
||||||
|
Logger.i("询问权限") |
||||||
|
launcher.launch(location_permissions) |
||||||
|
} |
||||||
|
} |
||||||
|
}) { |
||||||
|
Icon( |
||||||
|
painter = painterResource(id = R.drawable.ic_icon_location), |
||||||
|
contentDescription = null |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,12 @@ |
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
android:width="200dp" |
||||||
|
android:height="200dp" |
||||||
|
android:viewportWidth="1024" |
||||||
|
android:viewportHeight="1024"> |
||||||
|
<path |
||||||
|
android:fillColor="#FF000000" |
||||||
|
android:pathData="M789.6,187.35h-77.04c-7.17,0 -13.02,-5.85 -13.02,-13.02v-34.28c0,-13.43 -10.55,-24.88 -24.06,-25.21 -14.01,-0.41 -25.38,10.79 -25.38,24.72v34.85c0,7.17 -5.85,13.02 -13.02,13.02L389.48,187.44c-7.17,0 -13.02,-5.85 -13.02,-13.02v-34.36c0,-13.43 -10.55,-24.88 -24.06,-25.21 -14.01,-0.41 -25.38,10.79 -25.38,24.72v34.85c0,7.17 -5.85,13.02 -13.02,13.02h-79.6c-62.87,-0.08 -113.79,50.84 -113.79,113.71v494.22c0,62.87 50.92,113.79 113.79,113.79h555.11c62.87,0 113.79,-50.92 113.79,-113.79v-494.22c0.08,-62.87 -50.84,-113.79 -113.71,-113.79zM170.05,301.14c0,-35.6 28.84,-64.35 64.35,-64.35h79.6c7.17,0 13.02,5.85 13.02,13.02v34.36c0,13.43 10.55,24.88 24.06,25.21 14.01,0.41 25.38,-10.79 25.38,-24.72v-34.85c0,-7.17 5.85,-13.02 13.02,-13.02h247.6c7.17,0 13.02,5.85 13.02,13.02v34.36c0,13.43 10.55,24.88 24.06,25.21 14.01,0.41 25.38,-10.79 25.38,-24.72v-34.85c0,-7.17 5.85,-13.02 13.02,-13.02h77.04c35.6,0 64.35,28.84 64.35,64.35v37.41a16.95,16.95 0,0 1,-16.97 16.97L187.02,355.53a16.95,16.95 0,0 1,-16.97 -16.97v-37.41zM853.95,795.45c0,35.6 -28.84,64.35 -64.35,64.35L234.4,859.8c-35.51,0 -64.35,-28.84 -64.35,-64.35L170.05,421.94a16.95,16.95 0,0 1,16.97 -16.97h650.03a16.95,16.95 0,0 1,16.97 16.97l-0.08,373.51z" /> |
||||||
|
<path |
||||||
|
android:fillColor="#FF000000" |
||||||
|
android:pathData="M288.13,573.06h54.13c13.6,0 24.72,-11.04 24.72,-24.72 0,-13.6 -11.04,-24.72 -24.72,-24.72h-54.13c-13.6,0 -24.72,11.04 -24.72,24.72s11.04,24.72 24.72,24.72zM484.97,573.06h54.13c13.6,0 24.72,-11.04 24.72,-24.72 0,-13.6 -11.04,-24.72 -24.72,-24.72h-54.13c-13.6,0 -24.72,11.04 -24.72,24.72 0.08,13.68 11.12,24.72 24.72,24.72zM681.9,573.06h54.13c13.6,0 24.72,-11.04 24.72,-24.72 0,-13.6 -11.04,-24.72 -24.72,-24.72h-54.13c-13.6,0 -24.72,11.04 -24.72,24.72s11.04,24.72 24.72,24.72zM347.2,691.71L293.07,691.71c-13.6,0 -24.72,11.04 -24.72,24.72 0,13.6 11.04,24.72 24.72,24.72h54.13c13.6,0 24.72,-11.04 24.72,-24.72s-11.04,-24.72 -24.72,-24.72zM544.14,691.71L490,691.71c-13.6,0 -24.72,11.04 -24.72,24.72 0,13.6 11.04,24.72 24.72,24.72h54.13c13.6,0 24.72,-11.04 24.72,-24.72 -0.08,-13.68 -11.12,-24.72 -24.72,-24.72zM740.98,691.71h-54.14c-13.6,0 -24.72,11.04 -24.72,24.72 0,13.6 11.04,24.72 24.72,24.72h54.13c13.6,0 24.72,-11.04 24.72,-24.72s-11.04,-24.72 -24.72,-24.72z" /> |
||||||
|
</vector> |
@ -0,0 +1,9 @@ |
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
android:width="200dp" |
||||||
|
android:height="200dp" |
||||||
|
android:viewportWidth="1024" |
||||||
|
android:viewportHeight="1024"> |
||||||
|
<path |
||||||
|
android:fillColor="#FF000000" |
||||||
|
android:pathData="M698.98,181.33c-136.41,-141.54 -357.56,-141.54 -493.97,0 -136.41,141.57 -136.41,371.1 0,512.65l246.99,254.87 246.99,-254.87c136.41,-141.54 136.41,-371.08 0,-512.65zM451.99,533.84a109.18,109.18 0,0 1,-109.16 -109.22,109.2 109.2,0 0,1 109.16,-109.22 109.2,109.2 0,0 1,109.16 109.22,109.2 109.2,0 0,1 -109.16,109.22z" /> |
||||||
|
</vector> |
@ -0,0 +1,9 @@ |
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
android:width="200dp" |
||||||
|
android:height="200dp" |
||||||
|
android:viewportWidth="1024" |
||||||
|
android:viewportHeight="1024"> |
||||||
|
<path |
||||||
|
android:fillColor="#FF000000" |
||||||
|
android:pathData="M235.95,472.94l-45.23,45.31 210.09,209.51 432.36,-427.69 -45.01,-45.48 -387.16,382.98z" /> |
||||||
|
</vector> |
@ -0,0 +1,15 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<resources> |
||||||
|
<string name="apply_act_menu">申请活动</string> |
||||||
|
<string name="set_exam_menu">设置题库</string> |
||||||
|
<string name="rename_menu">社团命名</string> |
||||||
|
<string name="join_association">申请入团</string> |
||||||
|
<string name="update_exam">更新题库</string> |
||||||
|
<string name="post_answer">提交答案</string> |
||||||
|
<string name="denined_photo_permission">相册权限请求失败,无法读取相册</string> |
||||||
|
<string name="denined_location_permission">部分权限请求失败,无法获取地理位置</string> |
||||||
|
<string name="at">在</string> |
||||||
|
<string name="city">城市</string> |
||||||
|
<string name="address">地点</string> |
||||||
|
<string name="incity">市内找</string> |
||||||
|
</resources> |
@ -1,16 +0,0 @@ |
|||||||
<resources xmlns:tools="http://schemas.android.com/tools"> |
|
||||||
<!-- Base application theme. --> |
|
||||||
<style name="Theme.CSAMS" parent="Theme.MaterialComponents.DayNight.DarkActionBar"> |
|
||||||
<!-- Primary brand color. --> |
|
||||||
<item name="colorPrimary">@color/purple_200</item> |
|
||||||
<item name="colorPrimaryVariant">@color/purple_700</item> |
|
||||||
<item name="colorOnPrimary">@color/black</item> |
|
||||||
<!-- Secondary brand color. --> |
|
||||||
<item name="colorSecondary">@color/teal_200</item> |
|
||||||
<item name="colorSecondaryVariant">@color/teal_200</item> |
|
||||||
<item name="colorOnSecondary">@color/black</item> |
|
||||||
<!-- Status bar color. --> |
|
||||||
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item> |
|
||||||
<!-- Customize your theme here. --> |
|
||||||
</style> |
|
||||||
</resources> |
|
@ -0,0 +1,15 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<resources> |
||||||
|
<string name="apply_act_menu">申请活动</string> |
||||||
|
<string name="set_exam_menu">设置题库</string> |
||||||
|
<string name="rename_menu">社团命名</string> |
||||||
|
<string name="join_association">申请入团</string> |
||||||
|
<string name="update_exam">更新题库</string> |
||||||
|
<string name="post_answer">提交答案</string> |
||||||
|
<string name="denined_photo_permission">相册权限请求失败,无法读取相册</string> |
||||||
|
<string name="denined_location_permission">部分权限请求失败,无法获取地理位置</string> |
||||||
|
<string name="at">在</string> |
||||||
|
<string name="city">城市</string> |
||||||
|
<string name="address">地点</string> |
||||||
|
<string name="incity">市内找</string> |
||||||
|
</resources> |
@ -1,3 +1,15 @@ |
|||||||
<resources> |
<resources> |
||||||
|
|
||||||
|
<string name="apply_act_menu">申请活动</string> |
||||||
|
<string name="set_exam_menu">设置题库</string> |
||||||
|
<string name="rename_menu">社团命名</string> |
||||||
|
<string name="join_association">申请入团</string> |
||||||
|
<string name="update_exam">更新题库</string> |
||||||
|
<string name="post_answer">提交答案</string> |
||||||
|
<string name="denined_photo_permission">相册权限请求失败,无法读取相册</string> |
||||||
|
<string name="denined_location_permission">部分权限请求失败,无法获取地理位置</string> |
||||||
|
<string name="at">在</string> |
||||||
|
<string name="city">城市</string> |
||||||
|
<string name="address">地点</string> |
||||||
|
<string name="incity">市内找</string> |
||||||
</resources> |
</resources> |
Binary file not shown.
@ -1,4 +1,3 @@ |
|||||||
<?xml version="1.0" encoding="utf-8"?> |
<?xml version="1.0" encoding="utf-8"?> |
||||||
<manifest package="com.gyf.lib"> |
<manifest package="com.gyf.lib"> |
||||||
|
|
||||||
</manifest> |
</manifest> |
@ -0,0 +1,41 @@ |
|||||||
|
package com.gyf.lib.util |
||||||
|
|
||||||
|
import android.app.Activity |
||||||
|
import androidx.annotation.StringRes |
||||||
|
import androidx.compose.foundation.background |
||||||
|
import androidx.compose.foundation.layout.Arrangement |
||||||
|
import androidx.compose.foundation.layout.Row |
||||||
|
import androidx.compose.foundation.layout.Spacer |
||||||
|
import androidx.compose.foundation.layout.width |
||||||
|
import androidx.compose.material.MaterialTheme |
||||||
|
import androidx.compose.material.OutlinedButton |
||||||
|
import androidx.compose.material.Text |
||||||
|
import androidx.compose.runtime.Composable |
||||||
|
import androidx.compose.ui.Modifier |
||||||
|
import androidx.compose.ui.platform.LocalContext |
||||||
|
import androidx.compose.ui.unit.dp |
||||||
|
import com.gyf.lib.R |
||||||
|
|
||||||
|
@Composable |
||||||
|
fun BottomButton( |
||||||
|
modifier: Modifier = Modifier, |
||||||
|
@StringRes confirmDesc: Int = R.string.confirm_btn, |
||||||
|
@StringRes backDesc: Int = R.string.back_btn, |
||||||
|
onConfirm: () -> Unit |
||||||
|
) { |
||||||
|
val context = LocalContext.current as Activity |
||||||
|
Row(modifier = modifier, horizontalArrangement = Arrangement.Center) { |
||||||
|
OutlinedButton( |
||||||
|
onClick = onConfirm, |
||||||
|
modifier = Modifier.background(color = MaterialTheme.colors.primary) |
||||||
|
) { |
||||||
|
Text(text = context.getString(confirmDesc)) |
||||||
|
} |
||||||
|
Spacer(modifier = Modifier.width(10.dp)) |
||||||
|
OutlinedButton(onClick = { |
||||||
|
context.onBackPressed() |
||||||
|
}, modifier = Modifier.background(color = MaterialTheme.colors.secondary)) { |
||||||
|
Text(text = context.getString(backDesc)) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,4 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<resources> |
||||||
|
<integer name="activity_size">20</integer> |
||||||
|
</resources> |
@ -0,0 +1,15 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<resources> |
||||||
|
<string name="activity_application">活动申请书</string> |
||||||
|
<string name="activity_name">活动名称</string> |
||||||
|
<string name="activity_time">活动时间</string> |
||||||
|
<string name="confirm_btn">提交</string> |
||||||
|
<string name="back_btn">返回</string> |
||||||
|
<string name="reg_btn">注册</string> |
||||||
|
<string name="activity_address">活动地点</string> |
||||||
|
<string name="activity_size">活动人数</string> |
||||||
|
<string name="activity_size_error">活动人数要在%1d到%2d之间</string> |
||||||
|
<string name="activity_desc">活动介绍</string> |
||||||
|
<string name="search_btn">检索</string> |
||||||
|
<string name="search_null">未找到结果</string> |
||||||
|
</resources> |
@ -0,0 +1,4 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<resources> |
||||||
|
<integer name="activity_size">20</integer> |
||||||
|
</resources> |
@ -0,0 +1,15 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<resources> |
||||||
|
<string name="activity_application">活动申请书</string> |
||||||
|
<string name="activity_name">活动名称</string> |
||||||
|
<string name="activity_time">活动时间</string> |
||||||
|
<string name="confirm_btn">提交</string> |
||||||
|
<string name="back_btn">返回</string> |
||||||
|
<string name="reg_btn">注册</string> |
||||||
|
<string name="activity_address">活动地点</string> |
||||||
|
<string name="activity_size">活动人数</string> |
||||||
|
<string name="activity_size_error">活动人数要在%1d到%2d之间</string> |
||||||
|
<string name="activity_desc">活动介绍</string> |
||||||
|
<string name="search_btn">检索</string> |
||||||
|
<string name="search_null">未找到结果</string> |
||||||
|
</resources> |
@ -0,0 +1,4 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<resources> |
||||||
|
<integer name="activity_size">20</integer> |
||||||
|
</resources> |
@ -0,0 +1,15 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<resources> |
||||||
|
<string name="activity_application">活动申请书</string> |
||||||
|
<string name="activity_name">活动名称</string> |
||||||
|
<string name="activity_time">活动时间</string> |
||||||
|
<string name="confirm_btn">提交</string> |
||||||
|
<string name="back_btn">返回</string> |
||||||
|
<string name="reg_btn">注册</string> |
||||||
|
<string name="activity_address">活动地点</string> |
||||||
|
<string name="activity_size">活动人数</string> |
||||||
|
<string name="activity_size_error">活动人数要在%1d到%2d之间</string> |
||||||
|
<string name="activity_desc">活动介绍</string> |
||||||
|
<string name="search_btn">检索</string> |
||||||
|
<string name="search_null">未找到结果</string> |
||||||
|
</resources> |
Loading…
Reference in new issue