前几天我们成功上传了多张照片,但是我们的画面有点丑丑的,所以我们今天要把它修改成更漂亮,且下面会有指标,让我们可以知道总共有几张照片,以及现在的位置!!
我们这次是使用第三方套件,既然有现成的,就让我们直接使用吧! 它也有很多样式,可以让我们去自己使用。来源:https://github.com/zhpanvip/viewpagerindicator
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
//目前最新是1.2.1,之後如果有更新,就用最新的吧!
implementation 'com.github.zhpanvip:viewpagerindicator:1.2.1'
我们直接在fragment_add_invitation的地方新增 Indicator就好,且因为它是在FrameLayout,所以最下面的view会是最上层
<FrameLayout
android:id="@+id/fl_invitation_detail"
android:layout_width="match_parent"
android:layout_height="@dimen/image_height"
app:layout_constraintTop_toBottomOf="@id/toolbar_invitation_detail_fragment"
app:layout_constraintStart_toStartOf="parent">
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/banner_image"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<com.zhpan.indicator.IndicatorView
android:id="@+id/indicator_add_invitation"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center"
android:layout_margin="10dp"/>
</FrameLayout>
这边funtion我们会再等等从相簿拿到图片後,呼叫它
private fun setAdapterWitIndicator(list: List<Uri>){
binding.bannerImage.adapter = MultiplePhotoAdapter(list)
binding.indicatorAddInvitation.apply {
//设定没被选择的颜色/被选择的颜色
setSliderColor(Color.GRAY,ContextCompat.getColor(requireContext(),R.color.pewter_blue))
setSliderWidth(resources.getDimension(R.dimen.indicator_width))
//设定模式(更多模式可以往下看)
setSlideMode(IndicatorSlideMode.SCALE)
setIndicatorStyle(IndicatorStyle.CIRCLE)
//绑定viewPager2
.setupWithViewPager(binding.bannerImage)
}
}
Mode其他样式如下
有许多属性可以调整,有兴趣的小夥伴可以多去看看!
我们之前在从相簿拿到照片後,我们就先把它上传到Storage了,但这不太好,若使用者三心二意一直换照片,那我们的容量就爆啦!! 所以我们会是先把它显示在UI上,若是使用者按送出的Button之後,我们在进行储存
private var selectedUriList: List<Uri>? = null
private val resultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){ uri ->
if (uri.resultCode == Activity.RESULT_OK){
val selectedUri = uri.data?.clipData
if (selectedUri != null){
val list = mutableListOf<Uri>()
val account = selectedUri.itemCount
//这边一样透过for回圈把我们拿到的result改成uri
for (i in 0 until account){
val model = selectedUri.getItemAt(i).uri
list.add(model)
}
selectedUriList = list
//我们等等建立funtion让我们可以拿到type的名称,原本我们是在viewmodel做,但现在要改在Fragment里面先拿到file的type
getTypeFromSelectedUri(list)
//这边就直接用我们刚刚的套件,把list传进去,它就会帮我们生出indicator罗!
setAdapterWitIndicator(list)
}
}
}
我们直接在fragment里面先把type拿出来,并做成list,之後再传给viewModel
private fun getTypeFromSelectedUri(list: List<Uri>){
var typeList: MutableList<String> = mutableListOf()
for (i in 0 until list.size){
val model = Constant.getFileExtension(requireActivity(),list[i])
if (model != null) {
typeList.add(model)
}
}
selectedUriTypeList = typeList
}
原本我们是检查转换完的uri是否为null,来判定符不符合,但是现在我们要把它改成,只要resultLauncher拿到的不是null,就给过,因为就代表它有选了嘛 XD
private fun validDataFormAndSaveImage(): Boolean{
return when{
selectedUriList.isNullOrEmpty() ->{
showSnackBar(resources.getString(R.string.hint_select_your_image),true)
false
}
......
}
★不要全删喔,只是把图片check改成上面而已!
原本的方式是,我们在onClick的时候,把invitation加入到firestore,但是现在我们要修改成,当我们把照片传上去storage,然後全部转换成功成String後,我们直接在viewModel呼叫addInvitationToFireStore(),这样就可以确保当全部转换successful的时候,就呼叫传送Invitation。
binding.btnAddInvitationFragmentSubmit ->{
if (validDataFormAndSaveImage()){
showDialog(resources.getString(R.string.please_wait))
//一样先拿到UI的资料
val invitation = Invitation(
user_id = accountViewModel.userDetail.value!!.id,
user_name = accountViewModel.userDetail.value?.name,
user_image = accountViewModel.userDetail.value?.image,
pet_type = selectedPetType!!,
pet_type_description = binding.edAddInvitationPetTypeDescription.text.toString().trim(),
area = selectedArea!!,
date_place = binding.edAddInvitationDatePlace.text.toString().trim(),
date_time = selectedDate!!,
note = binding.edAddInvitationNote.text.toString().trim(),
update_time = Timestamp(Date(System.currentTimeMillis())),
)
//再把刚刚从相簿拿来的照片list跟invitation传进去
selectedUriList?.let {
matchingViewModel.saveImageToFireStorage(selectedUriTypeList,
it,invitation)
}
}
}
把fragment跟activity的参数拿掉,viewModel应该只要传入数据,并且透过livedata,让UI观察
//建立当图片fail的时候,可以让我们UI观察,我们不用加入successful的livedata,因为当成功的时候,它就会跑AddInvitation啦
private val _saveImage_fail = MutableLiveData<String>()
val saveImage_fail: LiveData<String>
get() = _saveImage_fail
//传入刚刚的typeList跟uriList,它们会是配对的,所以我们可以直接跑for回圈
fun saveImageToFireStorage( typeList: List<String>,uriList: List<Uri>,invitation: Invitation) {
val newList = mutableListOf<String>()
for (i in 0 until typeList.size){
val sdf: StorageReference = FirebaseStorage.getInstance().reference.child(
Constant.PET_IMAGE + "_" + System.currentTimeMillis() + "_" + typeList[i]
)
sdf.putFile(uriList[i])
.addOnSuccessListener { it ->
it.metadata?.reference?.downloadUrl
?.addOnSuccessListener { uri ->
val uriString = uri.toString()
newList.add(uriString)
//当所有的list都跑完後,我们就把刚刚的photoList加进去啦
if (i == uriList.size-1){
val newInvitation = Invitation(
user_id = invitation.user_id,
user_name = invitation.user_name,
user_image = invitation.user_image,
pet_type = invitation.pet_type,
pet_type_description = invitation.pet_type_description,
area = invitation.area,
date_place = invitation.date_place,
date_time = invitation.date_time,
note = invitation.note,
update_time = invitation.update_time,
photoUriList = newList
)
addInvitationToFireStore(newInvitation)
}
}
?.addOnFailureListener {
_saveImage_fail.postValue(it.toString())
}
}
.addOnFailureListener {
_saveImage_fail.postValue(it.toString())
}
}
}
fun resetSaveImageState(){
_saveImage_fail.postValue(null)
}
## 5.修改addInvitationToFireStore()
基本上就是新增livedata,让我们UI可以观察是上传成功了吗? 还是没有
private val _invitation_add_state = MutableLiveData<Boolean>()
val invitation_add_state: LiveData<Boolean>
get() = _invitation_add_state
fun addInvitationToFireStore(invitation: Invitation) {
Firebase.firestore.collection(Constant.INVITATION)
.add(invitation)
.addOnSuccessListener {
val mHashMap = HashMap<String, Any>()
mHashMap[Constant.ID] = it.id
it.update(mHashMap)
.addOnSuccessListener {
_invitation_add_state.postValue(true)
}
.addOnFailureListener {
_invitation_add_state.postValue(false)
}
}
.addOnFailureListener {
_invitation_add_state.postValue(false)
}
}
//我们也需要reset我们的livedata喔,不然会一直观察到之前的数据
fun resetAddInvitationState(){
_invitation_add_state.postValue(null)
}
matchingViewModel.invitation_add_state.observe( viewLifecycleOwner, androidx.lifecycle.Observer {
if (it == true){
hideDialog()
showSnackBar(resources.getString(R.string.add_invitation_successful),false)
findNavController().navigate(R.id.action_addInvitationFragment_to_navigation_home)
}else{
hideDialog()
showSnackBar(resources.getString(R.string.add_invitation_fail),true)
}
matchingViewModel.resetAddInvitationState()
})
还有观察我们的SaveImageState
matchingViewModel.saveImage_fail.observe(viewLifecycleOwner, androidx.lifecycle.Observer {
showSnackBar(it,true)
matchingViewModel.resetSaveImageState()
})
我们原本是单张照片,现在则是多张照片,简单!! 我们直接在各Adapter里面的ViewHolder里面修改原本的方式,改成以下就好!! 让我们显示第一张照片
item.photoUriList?.get(0)?.let { Constant.loadPetImage(it,binding.ivDashboardInvitationItemListImage) }
大功告成啦!!
那DetailFragment基本上就一样的方式啦,这边就不多说啦!
<<: 【Day 28】Google Apps Script - API Blueprint 篇 - Apiary 测试 API 介绍
>>: Day30_哇呜~最後一天的铁人实了~2021/10/13
Google Ads 会为你的帐户做评分,这项分数以 0% 到 100% 表示,100% 代表帐户能...
昨天看到运镜这词,大家是不是会想到拍电影呢?今天的主题也是跟电影有关的(?),大家看电影的时候,遇到...
辛苦赚钱之余也记得要好好享受生活,让这辈子过得更有趣 在菲律宾和柬埔寨的那段时光,是我最惬意的人生...
Azure machine learning: Upload data- 自己的资料自己传 要做汇率...
Aloha!又是我少女人妻 Uerica!铁人赛终於快结束拉,没想到默默的坚持到快要终点了,我家狗...