我正在实现Android智能电视的自定义屏幕键盘,就像 Youtube 一样。我不知道他们是如何创建这个自定义视图的。这是我对这种布局的思考:
我完全按照我的想法创建了这个布局。这是我的:
这是我在Github上正在进行的项目。请先试用Youtube版本,然后帮我解决问题。目前,我将所有重点事项留给Android来处理自己(在我的xml中在每个子视图中设置 focusable="true"
。我在这里只处理的是 ENTER_KEY_PAD 被按下。它将在我的布局顶部的Search EditText上显示文本。
我将展示一些代码片段(您可以克隆我的项目以获取更多详细信息)
CustomSoftKeyboardView.java
public class CustomSoftKeyboardView extends FrameLayout {
private RecyclerView mRvKeys;
private TextView mTvKeyDelete;
private TextView mTvKeyClear;
private TextView mTvKeyNumbers;
private TextView mTvKeySpace;
private TextView mTvKeySearch;
private int mContentLen;
private StringBuilder mDisplayedText;
private List<String> mCharacterKeyData;
private List<String> mNumberKeyData;
private KeyboardsAdapter mAdapter;
private View mCurrentFocusedView;
private View mNextFocusedView;
private boolean isChangeToNumPads;
public CustomSoftKeyboardView(@NonNull Context context) {
super(context);
initViews();
initNumberKeyPads();
initCharacterKeyPads();
initAdapter(context);
}
public CustomSoftKeyboardView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initViews();
initNumberKeyPads();
initCharacterKeyPads();
initAdapter(context);
}
public CustomSoftKeyboardView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initViews();
initNumberKeyPads();
initCharacterKeyPads();
initAdapter(context);
}
public CustomSoftKeyboardView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initViews();
initNumberKeyPads();
initCharacterKeyPads();
initAdapter(context);
}
private void initViews() {
mDisplayedText = new StringBuilder();
mContentLen = 0;
isChangeToNumPads = false;
View keyboardView = LayoutInflater.from(getContext()).inflate(R.layout.custom_keyboard_item, null);
mRvKeys = keyboardView.findViewById(R.id.rv_keyPads);
mTvKeyDelete = keyboardView.findViewById(R.id.tv_keyDelete);
mTvKeyDelete.setOnFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
ViewCompat.setBackground(v, ContextCompat.getDrawable(v.getContext(), R.drawable.pad_background_focus_light_drawable));
mTvKeyDelete.setTextColor(Color.WHITE);
} else {
ViewCompat.setBackground(v, null);
mTvKeyDelete.setTextColor(Color.BLACK);
}
}
});
mTvKeyClear = keyboardView.findViewById(R.id.tv_keyClear);
mTvKeyClear.setOnFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
ViewCompat.setBackground(v, ContextCompat.getDrawable(v.getContext(), R.drawable.pad_background_focus_light_drawable));
mTvKeyClear.setTextColor(Color.WHITE);
} else {
ViewCompat.setBackground(v, null);
mTvKeyClear.setTextColor(Color.BLACK);
}
}
});
mTvKeyNumbers = keyboardView.findViewById(R.id.tv_keyNumbers);
mTvKeyNumbers.setText(getContext().getString(R.string.number_pads));
mTvKeyNumbers.setOnFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
ViewCompat.setBackground(v, ContextCompat.getDrawable(v.getContext(), R.drawable.pad_background_focus_light_drawable));
mTvKeyNumbers.setTextColor(Color.WHITE);
} else {
ViewCompat.setBackground(v, null);
mTvKeyNumbers.setTextColor(Color.BLACK);
}
}
});
mTvKeySpace = keyboardView.findViewById(R.id.tv_space);
mTvKeySpace.setOnFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
ViewCompat.setBackground(v, ContextCompat.getDrawable(v.getContext(), R.drawable.pad_background_focus_light_drawable));
mTvKeySpace.setTextColor(Color.WHITE);
} else {
ViewCompat.setBackground(v, null);
mTvKeySpace.setTextColor(Color.BLACK);
}
}
});
mTvKeySearch = keyboardView.findViewById(R.id.tv_search);
mTvKeySearch.setOnFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
ViewCompat.setBackground(v, ContextCompat.getDrawable(v.getContext(), R.drawable.pad_background_focus_light_drawable));
mTvKeySearch.setTextColor(Color.WHITE);
} else {
ViewCompat.setBackground(v, null);
mTvKeySearch.setTextColor(Color.BLACK);
}
}
});
addView(keyboardView);
}
private void initCharacterKeyPads() {
mCharacterKeyData = new ArrayList<>();
mCharacterKeyData.add("A");
mCharacterKeyData.add("B");
mCharacterKeyData.add("C");
mCharacterKeyData.add("D");
mCharacterKeyData.add("E");
mCharacterKeyData.add("F");
mCharacterKeyData.add("G");
mCharacterKeyData.add("H");
mCharacterKeyData.add("I");
mCharacterKeyData.add("J");
mCharacterKeyData.add("K");
mCharacterKeyData.add("L");
mCharacterKeyData.add("M");
mCharacterKeyData.add("N");
mCharacterKeyData.add("O");
mCharacterKeyData.add("P");
mCharacterKeyData.add("Q");
mCharacterKeyData.add("R");
mCharacterKeyData.add("S");
mCharacterKeyData.add("T");
mCharacterKeyData.add("U");
mCharacterKeyData.add("V");
mCharacterKeyData.add("W");
mCharacterKeyData.add("X");
mCharacterKeyData.add("Y");
mCharacterKeyData.add("Z");
mCharacterKeyData.add("-");
mCharacterKeyData.add("'");
}
private void initNumberKeyPads() {
mNumberKeyData = new ArrayList<>();
mNumberKeyData.add("1");
mNumberKeyData.add("2");
mNumberKeyData.add("3");
mNumberKeyData.add("&");
mNumberKeyData.add("#");
mNumberKeyData.add("(");
mNumberKeyData.add(")");
mNumberKeyData.add("4");
mNumberKeyData.add("5");
mNumberKeyData.add("6");
mNumberKeyData.add("@");
mNumberKeyData.add("!");
mNumberKeyData.add("?");
mNumberKeyData.add(":");
mNumberKeyData.add("7");
mNumberKeyData.add("8");
mNumberKeyData.add("9");
mNumberKeyData.add("0");
mNumberKeyData.add(".");
mNumberKeyData.add("_");
mNumberKeyData.add("\"");
}
private void initAdapter(Context context) {
mAdapter = new KeyboardsAdapter();
mRvKeys.setAdapter(mAdapter);
mAdapter.setKeyData(mCharacterKeyData);
GridLayoutManager gridLayoutManager = new GridLayoutManager(context, 7);
mRvKeys.setLayoutManager(gridLayoutManager);
}
public void reset() {
if (mDisplayedText.length() > 0) {
mDisplayedText.delete(0, mDisplayedText.length());
}
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_UP) {
if (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) {
View selectedView = findFocus();
switch (selectedView.getId()) {
case R.id.tv_keyDelete:
if (mDisplayedText.length() > 0) {
mDisplayedText.deleteCharAt(mContentLen);
mContentLen = mDisplayedText.length() - 1;
EventBus.getDefault().post(new EventDisplayText(mDisplayedText));
}
break;
case R.id.tv_keyClear:
if (mDisplayedText.length() > 0) {
mDisplayedText.delete(0, mDisplayedText.length());
mContentLen = 0;
EventBus.getDefault().post(new EventDisplayText(mDisplayedText));
}
break;
case R.id.tv_keyNumbers:
if (isChangeToNumPads) {
isChangeToNumPads = false;
mTvKeyNumbers.setText(getContext().getString(R.string.number_pads));
mAdapter.setKeyData(mCharacterKeyData);
} else {
isChangeToNumPads = true;
mTvKeyNumbers.setText(getContext().getString(R.string.character_pads));
mAdapter.setKeyData(mNumberKeyData);
}
break;
case R.id.tv_space:
mDisplayedText.append(" ");
EventBus.getDefault().post(new EventDisplayText(mDisplayedText));
break;
case R.id.tv_search:
if (mDisplayedText.length() > 0) {
EventBus.getDefault().post(new EventSearching(mDisplayedText.toString()));
}
Toast.makeText(getContext(), "PRESS SEARCH", Toast.LENGTH_SHORT).show();
break;
default:
mDisplayedText.append(getKeyPadData(findFocus()));
mContentLen = mDisplayedText.length() - 1;
EventBus.getDefault().post(new EventDisplayText(mDisplayedText));
break;
}
}
}
return super.dispatchKeyEvent(event);
}
private String getKeyPadData(View view) {
TextView textView = view.findViewById(R.id.tv_keyPad);
return textView.getText().toString();
}
}
MainActivity.java
public class MainActivity extends Activity {
private EditText mEdtSearch;
private CustomSoftKeyboardView customSoftKeyboardView;
private RecyclerView mRvRecentSearches;
private RecentSearchesAdapter mRecentSearchesAdapter;
private LinearLayout mRoot;
private View currentFocusedView;
private View nextFocusedView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRoot = findViewById(R.id.root);
getWindow().getDecorView().setBackgroundColor(ContextCompat.getColor(this, R.color.activity_light_bg));
mEdtSearch = findViewById(R.id.edt_search);
mRvRecentSearches = findViewById(R.id.rv_recentSearches);
customSoftKeyboardView = findViewById(R.id.customKeyboardView);
setUpSuggestionsAdapter();
}
private void setUpSuggestionsAdapter() {
mRecentSearchesAdapter = new RecentSearchesAdapter();
mRvRecentSearches.setAdapter(mRecentSearchesAdapter);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
mRvRecentSearches.setLayoutManager(linearLayoutManager);
}
private List<String> generateSuggestionsList() {
List<String> suggestionsList = new ArrayList<>();
suggestionsList.add("Doraemon");
suggestionsList.add("Lord of The Rings");
suggestionsList.add("Hannibal");
suggestionsList.add("How To Train Your Dragon");
suggestionsList.add("Game of Thrones Season 6");
suggestionsList.add("The Walking Dead Season 4");
suggestionsList.add("Geostorm");
suggestionsList.add("Inception");
suggestionsList.add("Jurassic World: Fallen Kingdom");
suggestionsList.add("Deadpool 2");
suggestionsList.add("How I Met Your Mother");
suggestionsList.add("Tron: Legacy");
return suggestionsList;
}
@Override
protected void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
protected void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void displayText(EventDisplayText eventDisplayText) {
if (eventDisplayText != null) {
mEdtSearch.setText(eventDisplayText.getDisplayedText().toString());
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void showRecentSearches(EventSearching eventSearching) {
if (eventSearching != null) {
mRecentSearchesAdapter.setSearchContent(eventSearching.getSearchContent());
mEdtSearch.setText(null);
customSoftKeyboardView.reset();
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void receiveFocusedViews(EventSendFocusedView eventSendFocusedView) {
if (eventSendFocusedView != null) {
currentFocusedView = eventSendFocusedView.getCurrentFocusedView();
nextFocusedView = eventSendFocusedView.getNextFocusedView();
}
}
}
activity_main.xml中
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/root"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.tv.demo.softkeyboardfortv.MainActivity">
<EditText
android:id="@+id/edt_search"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginStart="50dp"
android:layout_marginEnd="50dp"
android:textColor="#000000"
android:focusable="false"
android:textColorHint="#000000"
android:backgroundTint="#000000"
android:hint="Search"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="10dp">
<com.tv.demo.softkeyboardfortv.CustomSoftKeyboardView
android:id="@+id/customKeyboardView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true" />
<LinearLayout
android:id="@+id/left_search_list"
android:layout_alignParentStart="true"
android:layout_marginStart="53dp"
android:layout_toStartOf="@id/customKeyboardView"
android:descendantFocusability="blocksDescendants"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:text="RECENT SEARCHES:"
android:textColor="#000000"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_recentSearches"
android:layout_marginTop="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</RelativeLayout>
</LinearLayout>
custom_keyboard_item.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:focusable="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/keyPadsContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_keyPads"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<LinearLayout
android:id="@+id/bottomRoot"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginStart="10dp"
android:layout_marginTop="10dp"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_space"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:focusable="true"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:text="SPACE"
android:textColor="#000000"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tv_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:focusable="true"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:text="SEARCH"
android:textColor="#000000"
android:textSize="18sp"
android:textStyle="bold" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/rightRoot"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_toEndOf="@id/keyPadsContainer"
android:orientation="vertical">
<TextView
android:id="@+id/tv_keyDelete"
style="@style/RightKeyPads"
android:focusable="true"
android:nextFocusDown="@id/tv_keyClear"
android:text="@string/delete_pad" />
<TextView
android:id="@+id/tv_keyClear"
style="@style/RightKeyPads"
android:focusable="true"
android:nextFocusDown="@id/tv_keyNumbers"
android:text="@string/clear_pad" />
<TextView
android:id="@+id/tv_keyNumbers"
style="@style/RightKeyPads"
android:focusable="true" />
</LinearLayout>
</RelativeLayout>
我有一些问题要问:
在关键垫上: A , H , O , V , SPACE 即可。当我按 DPAD_LEFT 时,如何让它专注于RecentSearches RecyclerView的第一项(总是第一项)
与键盘相同: V , W , X , Y , Z , - ,&#39; ,按 DPAD_DOWN 时,始终关注 SPACE < / p>
当前焦点位于Recent Searches RecyclerView项目(任何索引)时,当我按 DPAD_RIGHT 时,它必须返回到之前的精确聚焦视图(例如:V(当前聚焦)查看) - &gt;按 DPAD_LEFT - &gt;最近搜索的第一个索引RecyclerView项目(当前关注的) - &gt;按 DPAD_DOWN - &gt;第二个项目(dota 2 wtf) (以当前为中心) - &gt;按 DPAD_RIGHT - &gt; V
我很欣赏我的3个问题。谢谢你的阅读。