mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
fix: SymbolInputView is invalidated multiple times on tab reselection
This commit is contained in:
@@ -23,17 +23,17 @@ import static com.itsaky.androidide.utils.ResourceUtilsKt.resolveAttr;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.DiffUtil;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.databinding.LayoutSymbolItemBinding;
|
||||
import com.itsaky.androidide.ui.SymbolInputView.Symbol;
|
||||
import com.itsaky.androidide.editor.ui.IDEEditor;
|
||||
|
||||
import com.itsaky.androidide.models.Symbol;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class SymbolInputAdapter extends RecyclerView.Adapter<SymbolInputAdapter.VH> {
|
||||
|
||||
@@ -49,24 +49,54 @@ public class SymbolInputAdapter extends RecyclerView.Adapter<SymbolInputAdapter.
|
||||
pairs.add('>');
|
||||
}
|
||||
|
||||
private final IDEEditor editor;
|
||||
private Symbol[] symbols;
|
||||
private IDEEditor editor;
|
||||
private List<Symbol> symbols;
|
||||
|
||||
public SymbolInputAdapter(IDEEditor editor) {
|
||||
this(editor, null);
|
||||
}
|
||||
|
||||
public SymbolInputAdapter(IDEEditor editor, Symbol[] symbols) {
|
||||
public SymbolInputAdapter(IDEEditor editor, List<Symbol> symbols) {
|
||||
this.editor = editor;
|
||||
this.symbols = symbols == null ? new Symbol[0] : symbols;
|
||||
this.symbols = symbols == null ? Collections.emptyList() : symbols;
|
||||
setHasStableIds(true);
|
||||
}
|
||||
public void refresh(IDEEditor editor, List<Symbol> newSymbols) {
|
||||
this.editor = Objects.requireNonNull(editor);
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
public void setSymbols(boolean notify, Symbol... symbols) {
|
||||
this.symbols = symbols;
|
||||
if (notify) {
|
||||
notifyDataSetChanged();
|
||||
if (this.symbols.equals(newSymbols)) {
|
||||
// no need to update symbols
|
||||
return;
|
||||
}
|
||||
|
||||
Objects.requireNonNull(newSymbols, "Symbol list cannot be null");
|
||||
|
||||
final var callback = new DiffUtil.Callback() {
|
||||
@Override
|
||||
public int getOldListSize() {
|
||||
return symbols.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNewListSize() {
|
||||
return newSymbols.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
|
||||
return Objects.equals(symbols.get(oldItemPosition), newSymbols.get(newItemPosition));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
|
||||
return areItemsTheSame(oldItemPosition, newItemPosition);
|
||||
}
|
||||
};
|
||||
|
||||
final var result = DiffUtil.calculateDiff(callback);
|
||||
|
||||
this.symbols = newSymbols;
|
||||
result.dispatchUpdatesTo(this);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -78,8 +108,10 @@ public class SymbolInputAdapter extends RecyclerView.Adapter<SymbolInputAdapter.
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull VH holder, int position) {
|
||||
if (symbols == null || symbols[position] == null) return;
|
||||
final Symbol symbol = symbols[position];
|
||||
if (symbols == null || symbols.get(position) == null) {
|
||||
return;
|
||||
}
|
||||
final Symbol symbol = symbols.get(position);
|
||||
holder.binding.symbol.setText(symbol.getLabel());
|
||||
holder.binding.symbol.setTextColor(
|
||||
resolveAttr(holder.binding.symbol.getContext(), R.attr.colorOnSurface));
|
||||
@@ -87,9 +119,14 @@ public class SymbolInputAdapter extends RecyclerView.Adapter<SymbolInputAdapter.
|
||||
__ -> insertSymbol(symbol.getCommit(), symbol.getOffset()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return symbols.get(position).hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return symbols == null ? 0 : symbols.length;
|
||||
return symbols == null ? 0 : symbols.size();
|
||||
}
|
||||
|
||||
void insertSymbol(String text, int selectionOffset) {
|
||||
@@ -131,6 +168,7 @@ public class SymbolInputAdapter extends RecyclerView.Adapter<SymbolInputAdapter.
|
||||
}
|
||||
|
||||
public static class VH extends RecyclerView.ViewHolder {
|
||||
|
||||
LayoutSymbolItemBinding binding;
|
||||
|
||||
public VH(LayoutSymbolItemBinding binding) {
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.itsaky.androidide.models
|
||||
|
||||
/**
|
||||
* A symbol that is shown in the [SymbolInputView][com.itsaky.androidide.ui.SymbolInputView].
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
open class Symbol @JvmOverloads constructor(
|
||||
open val label: String,
|
||||
open val commit: String,
|
||||
open val offset: Int = 1
|
||||
) {
|
||||
@JvmOverloads
|
||||
constructor(both: String, offset: Int = 1) : this(both, both, offset)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is Symbol) return false
|
||||
|
||||
if (label != other.label) return false
|
||||
if (commit != other.commit) return false
|
||||
if (offset != other.offset) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = label.hashCode()
|
||||
result = 31 * result + commit.hashCode()
|
||||
result = 31 * result + offset
|
||||
return result
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "Symbol(label='$label', commit='$commit', offset=$offset)"
|
||||
}
|
||||
}
|
||||
@@ -244,8 +244,7 @@ constructor(
|
||||
}
|
||||
|
||||
fun refreshSymbolInput(editor: CodeEditorView) {
|
||||
binding.symbolInput.bindEditor(editor.editor)
|
||||
binding.symbolInput.setSymbols(*forFile(editor.file))
|
||||
binding.symbolInput.refresh(editor.editor, forFile(editor.file))
|
||||
}
|
||||
|
||||
fun onSoftInputChanged() {
|
||||
|
||||
@@ -23,12 +23,14 @@ import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.itsaky.androidide.adapters.SymbolInputAdapter;
|
||||
import com.itsaky.androidide.editor.ui.IDEEditor;
|
||||
import com.itsaky.androidide.models.Symbol;
|
||||
import com.itsaky.androidide.utils.Symbols;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class SymbolInputView extends RecyclerView {
|
||||
|
||||
private SymbolInputAdapter adapter;
|
||||
|
||||
public SymbolInputView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
@@ -42,55 +44,16 @@ public class SymbolInputView extends RecyclerView {
|
||||
setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false));
|
||||
}
|
||||
|
||||
public void bindEditor(IDEEditor editor) {
|
||||
adapter = new SymbolInputAdapter(editor);
|
||||
setAdapter(adapter);
|
||||
}
|
||||
|
||||
public void setSymbols(Symbol... symbols) {
|
||||
if (adapter != null) {
|
||||
if (symbols.length == 0) {
|
||||
symbols = Symbols.INSTANCE.plainTextSymbols();
|
||||
}
|
||||
|
||||
adapter.setSymbols(true, symbols);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Symbol {
|
||||
|
||||
private final String label;
|
||||
private final String commit;
|
||||
private final int offset;
|
||||
|
||||
public Symbol(String both) {
|
||||
this(both, 1);
|
||||
public void refresh(IDEEditor editor, List<Symbol> symbols) {
|
||||
if (symbols == null || symbols.isEmpty()) {
|
||||
symbols = Symbols.INSTANCE.getPlainTextSymbols();
|
||||
}
|
||||
|
||||
public Symbol(String both, int offset) {
|
||||
this(both, both, offset);
|
||||
}
|
||||
|
||||
public Symbol(String label, String commit, int offset) {
|
||||
this.label = label;
|
||||
this.commit = commit;
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
public Symbol(String label, String commit) {
|
||||
this(label, commit, 1);
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public String getCommit() {
|
||||
return commit;
|
||||
}
|
||||
|
||||
public int getOffset() {
|
||||
return offset;
|
||||
final var adapter = getAdapter();
|
||||
if (adapter instanceof SymbolInputAdapter) {
|
||||
((SymbolInputAdapter) adapter).refresh(editor, symbols);
|
||||
} else {
|
||||
setAdapter(new SymbolInputAdapter(editor));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,28 +17,29 @@
|
||||
*/
|
||||
package com.itsaky.androidide.utils
|
||||
|
||||
import com.itsaky.androidide.ui.SymbolInputView.Symbol
|
||||
import com.itsaky.androidide.models.Symbol
|
||||
import java.io.File
|
||||
|
||||
object Symbols {
|
||||
|
||||
@JvmStatic
|
||||
fun forFile(file: File?): Array<Symbol?> {
|
||||
fun forFile(file: File?): List<Symbol> {
|
||||
if (file == null || !file.isFile) {
|
||||
return emptyArray()
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
return when (file.extension) {
|
||||
"java",
|
||||
"gradle",
|
||||
"kt" -> javaSymbols()
|
||||
"xml" -> xmlSymbols()
|
||||
else -> plainTextSymbols()
|
||||
"kt" -> javaSymbols
|
||||
|
||||
"xml" -> xmlSymbols
|
||||
else -> plainTextSymbols
|
||||
}
|
||||
}
|
||||
|
||||
fun javaSymbols(): Array<Symbol?> {
|
||||
return arrayOf(
|
||||
private val javaSymbols by lazy {
|
||||
listOf(
|
||||
TabSymbol(),
|
||||
Symbol("{", "{}"),
|
||||
Symbol("}"),
|
||||
@@ -64,8 +65,8 @@ object Symbols {
|
||||
)
|
||||
}
|
||||
|
||||
fun xmlSymbols(): Array<Symbol?> {
|
||||
return arrayOf(
|
||||
private val xmlSymbols by lazy {
|
||||
listOf(
|
||||
TabSymbol(),
|
||||
Symbol("<", "<>"),
|
||||
Symbol(">"),
|
||||
@@ -93,8 +94,8 @@ object Symbols {
|
||||
)
|
||||
}
|
||||
|
||||
fun plainTextSymbols(): Array<Symbol?> {
|
||||
return arrayOf(
|
||||
val plainTextSymbols by lazy {
|
||||
listOf(
|
||||
TabSymbol(),
|
||||
Symbol("{", "{}"),
|
||||
Symbol("}"),
|
||||
@@ -121,12 +122,11 @@ object Symbols {
|
||||
}
|
||||
|
||||
private class TabSymbol : Symbol("↹") {
|
||||
override fun getCommit(): String {
|
||||
return "\t"
|
||||
}
|
||||
|
||||
override fun getOffset(): Int {
|
||||
return 1
|
||||
}
|
||||
override val commit: String
|
||||
get() = "\t"
|
||||
|
||||
override val offset: Int
|
||||
get() = 1
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user