Closed qianqianNotqianqian closed 6 months ago
在配合如下代码会出现这种情况:package mapleleaf.materialdesign.engine.ui.fragments
import android.animation.ObjectAnimator import android.annotation.SuppressLint import android.content.ActivityNotFoundException import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.content.pm.ResolveInfo import android.os.Bundle import android.text.Editable import android.text.Spannable import android.text.SpannableString import android.text.style.ForegroundColorSpan import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.inputmethod.EditorInfo import android.widget.Button import android.widget.EditText import android.widget.ImageView import android.widget.LinearLayout import android.widget.TextView import androidx.core.content.ContextCompat import androidx.core.view.isVisible import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import com.bumptech.glide.Glide import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions import com.google.android.material.card.MaterialCardView import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import mapleleaf.materialdesign.engine.R import mapleleaf.materialdesign.engine.base.UniversalFragmentBase import mapleleaf.materialdesign.engine.ui.dialog.DialogHelper import mapleleaf.materialdesign.engine.utils.SearchTextWatcher import mapleleaf.materialdesign.engine.utils.toast import me.zhanghai.android.fastscroll.FastScrollerBuilder import java.util.regex.Pattern
class FragmentComponentActivities : UniversalFragmentBase() {
private lateinit var recyclerView: RecyclerView private lateinit var adapter: AdapterComponents private lateinit var swipeRefreshLayout: SwipeRefreshLayout override val layoutResId: Int get() = R.layout.fragment_components override fun onViewCreated(rootView: View, savedInstanceState: Bundle?) { super.onViewCreated(rootView, savedInstanceState) val packageName = arguments?.getString("packageName") ?: return val imageView = rootView.findViewById<ImageView>(R.id.null_list) val appsSearchBox = rootView.findViewById<EditText>(R.id.apps_search_box) recyclerView = rootView.findViewById(R.id.recyclerView) swipeRefreshLayout = rootView.findViewById(R.id.swipeRefreshLayout) recyclerView.layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false) adapter = AdapterComponents(requireContext(), emptyList()) recyclerView.adapter = adapter FastScrollerBuilder(recyclerView).build() val colorRed = ContextCompat.getColor(requireContext(), R.color.red1) val colorGreen = ContextCompat.getColor(requireContext(), R.color.lawngreen) val colorBlue = ContextCompat.getColor(requireContext(), R.color.blue) val colorOrange = ContextCompat.getColor(requireContext(), R.color.orange2) val progressColors = ContextCompat.getColor(requireContext(), R.color.swipe_refresh_layout_progress) swipeRefreshLayout.setColorSchemeColors(colorRed, colorGreen, colorBlue, colorOrange) swipeRefreshLayout.setProgressBackgroundColorSchemeColor(progressColors) lifecycleScope.launch(Dispatchers.Main) { val activities = withContext(Dispatchers.IO) { getActivities(packageName) } val visibility = if (activities.isEmpty()) View.GONE else View.VISIBLE adapter.setData(activities.toList()) recyclerView.adapter = adapter recyclerView.visibility = visibility appsSearchBox.visibility = visibility swipeRefreshLayout.visibility = visibility imageView.visibility = if (visibility == View.VISIBLE) View.GONE else View.VISIBLE val defaultSearchText = appsSearchBox.text setupSearchBox(appsSearchBox) searchApp(defaultSearchText) } swipeRefreshLayout.setOnRefreshListener { lifecycleScope.launch(Dispatchers.Main) { if (::adapter.isInitialized) { val searchQuery = appsSearchBox.text.toString() if (searchQuery.isNotBlank()) { searchApp(appsSearchBox.text) swipeRefreshLayout.isRefreshing = false } else { val activities = withContext(Dispatchers.IO) { getActivities(packageName) } adapter.setData(activities.toList()) swipeRefreshLayout.isRefreshing = false } } } } } private fun setupSearchBox(appsSearchBox: EditText) { appsSearchBox.setOnEditorActionListener { _, actionId, _ -> if (actionId == EditorInfo.IME_ACTION_DONE || actionId == EditorInfo.IME_ACTION_NEXT || actionId == EditorInfo.IME_ACTION_SEARCH) { searchApp(appsSearchBox.text) } true } val searchTextWatcher = SearchTextWatcher { searchApp(appsSearchBox.text) } appsSearchBox.addTextChangedListener(searchTextWatcher) } private fun searchApp(text: Editable?) { val searchText = text?.toString() ?: "" if (searchText.isNotEmpty()) { val filteredProviders = getFilteredActivities(searchText) adapter.setData(filteredProviders) } else { val packageName = arguments?.getString("packageName") ?: return val providers = getActivities(packageName) adapter.setData(providers.toList()) } adapter.setSearchText(searchText) } private fun getFilteredActivities(searchText: String): List<ResolveInfo> { val packageName = arguments?.getString("packageName") ?: return emptyList() val allActivities = getActivities(packageName) val packageManager = requireContext().packageManager return allActivities.filter { activity -> val activityName = activity.activityInfo.name val activityLabel = activity.loadLabel(packageManager).toString() activityName.contains(searchText, ignoreCase = true) || activityLabel.contains( searchText, ignoreCase = true ) } } private fun getActivities(packageName: String): List<ResolveInfo> { return try { val packageInfo = requireContext().packageManager.getPackageInfo( packageName, PackageManager.GET_ACTIVITIES ) packageInfo.activities?.map { activity -> ResolveInfo().apply { this.activityInfo = activity } }?.sortedBy { it.activityInfo.name } ?: emptyList() } catch (e: PackageManager.NameNotFoundException) { e.printStackTrace() emptyList() } } class AdapterComponents( private val context: Context, var activityList: List<ResolveInfo>, ) : RecyclerView.Adapter<AdapterComponents.ViewHolder>() { private var searchText: String = ""
// private var diffCallback: DiffCallback? = null
@SuppressLint("NotifyDataSetChanged") fun setSearchText(text: String) { searchText = text notifyDataSetChanged() } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val view = LayoutInflater.from(parent.context) .inflate(R.layout.list_item_component, parent, false) return ViewHolder(view) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { val resolveInfo = activityList[position] holder.bind(resolveInfo) } override fun getItemCount(): Int { return activityList.size } @SuppressLint("NotifyDataSetChanged") fun setData(newData: List<ResolveInfo>) {
// diffCallback?.let { // val result = DiffUtil.calculateDiff(it) // activityList = newData // result.dispatchUpdatesTo(this) // } // diffCallback = DiffCallback(activityList, newData) activityList = newData notifyDataSetChanged() }
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener { private val activityIcon: ImageView = itemView.findViewById(R.id.ItemIcon) private val activityLabelTextView: TextView = itemView.findViewById(R.id.ItemTitle) private val activityNameTextView: TextView = itemView.findViewById(R.id.ItemText) private val componentMaterialCardView: MaterialCardView = itemView.findViewById(R.id.componentMaterialCardView) init { componentMaterialCardView.setOnClickListener(this) } @SuppressLint("InflateParams") override fun onClick(v: View?) { val activitiesInfo = activityList[bindingAdapterPosition] val layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater val dialogView = layoutInflater.inflate(R.layout.dialog_components_detail, null) val dialog = DialogHelper.customDialog(context, dialogView) dialogView.apply { findViewById<Button>(R.id.action_start).isVisible = true findViewById<LinearLayout>(R.id.activity_full_name).isVisible = true findViewById<LinearLayout>(R.id.package_full_name).isVisible = true findViewById<LinearLayout>(R.id.component_full_name).isVisible = false } fun setTextAndColor(textView: TextView, value: Boolean) { textView.text = value.toString() textView.setTextColor( ContextCompat.getColor( context, if (value) R.color.green else R.color.red ) ) } val enabledTextView = dialogView.findViewById<TextView>(R.id.state_enable) setTextAndColor(enabledTextView, activitiesInfo.activityInfo.enabled) val exportedTextView = dialogView.findViewById<TextView>(R.id.state_exported) setTextAndColor(exportedTextView, activitiesInfo.activityInfo.exported) dialogView.findViewById<ImageView>(R.id.imageView_icon) .setImageDrawable(activitiesInfo.activityInfo.loadIcon(context.packageManager))
// Glide.with(context) // .load(activitiesInfo.activityInfo.loadIcon(context.packageManager)) // .error(R.drawable.ic_error) // .diskCacheStrategy(DiskCacheStrategy.ALL) // .transition(DrawableTransitionOptions.withCrossFade(300)) // .into(imageViewIcon)
fun setTextToEditText(editText: EditText, text: String) { editText.setText(text) } setTextToEditText( dialogView.findViewById(R.id.edit_title), activitiesInfo.activityInfo.loadLabel(context.packageManager).toString() ) setTextToEditText( dialogView.findViewById(R.id.edit_pkg), activitiesInfo.activityInfo.packageName ) setTextToEditText( dialogView.findViewById(R.id.edit_activity), activitiesInfo.activityInfo.name ) dialogView.findViewById<View>(R.id.btn_cancel) .setOnClickListener { dialog.dismiss() } dialogView.findViewById<View>(R.id.action_start).setOnClickListener { dialog.dismiss() if (activitiesInfo.activityInfo.exported) { val intent = Intent().apply { component = ComponentName( activitiesInfo.activityInfo.packageName, activitiesInfo.activityInfo.name ) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) } try { context.startActivity(intent) } catch (e: ActivityNotFoundException) { toast("启动 Activity 失败") } catch (e: SecurityException) { toast("您没有权限运行此 Activity") } } else { try { // 构建 shell 命令 val command = "am start -n ${activitiesInfo.activityInfo.packageName}/${activitiesInfo.activityInfo.name}" // 获取 root 权限并执行命令 Runtime.getRuntime().exec(arrayOf("su", "-c", command)) } catch (e: SecurityException) { toast("您没有权限运行此 Activity") } catch (e: Exception) { // 处理其他异常情况 toast("启动 Activity 失败") } } } } fun bind(resolveInfo: ResolveInfo) { val activityLabel = resolveInfo.loadLabel(context.packageManager).toString() val activityName = resolveInfo.activityInfo.name if (!resolveInfo.activityInfo.exported) { activityNameTextView.setTextColor(ContextCompat.getColor(context, R.color.red)) } else { activityNameTextView.setTextColor( ContextCompat.getColor( context, R.color.green ) ) } activityNameTextView.text = activityName.highlightText(searchText) activityLabelTextView.text = activityLabel.highlightText(searchText) Glide.with(context) .load(resolveInfo.activityInfo.loadIcon(context.packageManager)) .error(R.drawable.ic_error) .diskCacheStrategy(DiskCacheStrategy.ALL) .transition(DrawableTransitionOptions.withCrossFade(300)) .into(activityIcon) } } }
} 当条目高度不一时,就会乱跳,而且当我拖动滚动条就会因跳动导致整个列表都乱跳
这种情况仅在和recycleview绑定时出现
默认不支持高度不一致的条目,请仔细阅读 README。
在配合如下代码会出现这种情况:package mapleleaf.materialdesign.engine.ui.fragments
import android.animation.ObjectAnimator import android.annotation.SuppressLint import android.content.ActivityNotFoundException import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.content.pm.ResolveInfo import android.os.Bundle import android.text.Editable import android.text.Spannable import android.text.SpannableString import android.text.style.ForegroundColorSpan import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.inputmethod.EditorInfo import android.widget.Button import android.widget.EditText import android.widget.ImageView import android.widget.LinearLayout import android.widget.TextView import androidx.core.content.ContextCompat import androidx.core.view.isVisible import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import com.bumptech.glide.Glide import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions import com.google.android.material.card.MaterialCardView import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import mapleleaf.materialdesign.engine.R import mapleleaf.materialdesign.engine.base.UniversalFragmentBase import mapleleaf.materialdesign.engine.ui.dialog.DialogHelper import mapleleaf.materialdesign.engine.utils.SearchTextWatcher import mapleleaf.materialdesign.engine.utils.toast import me.zhanghai.android.fastscroll.FastScrollerBuilder import java.util.regex.Pattern
class FragmentComponentActivities : UniversalFragmentBase() {
// private var diffCallback: DiffCallback? = null
// diffCallback?.let { // val result = DiffUtil.calculateDiff(it) // activityList = newData // result.dispatchUpdatesTo(this) // } // diffCallback = DiffCallback(activityList, newData) activityList = newData notifyDataSetChanged() }
// Glide.with(context) // .load(activitiesInfo.activityInfo.loadIcon(context.packageManager)) // .error(R.drawable.ic_error) // .diskCacheStrategy(DiskCacheStrategy.ALL) // .transition(DrawableTransitionOptions.withCrossFade(300)) // .into(imageViewIcon)
} 当条目高度不一时,就会乱跳,而且当我拖动滚动条就会因跳动导致整个列表都乱跳