增加搜索功能

This commit is contained in:
litingting 2024-10-17 18:33:31 +08:00
parent bd31ac8780
commit f341f3efe6
18 changed files with 616 additions and 59 deletions

View File

@ -0,0 +1,46 @@
package com.hi.music.player.adapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.hi.music.player.databinding.ItemSuggestionBinding;
public class AdapterSuggestion extends BaseAdapter<String, ItemSuggestionBinding> {
@Override
protected ItemSuggestionBinding getViewBinding(ViewGroup parent) {
return ItemSuggestionBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
VHolder<ItemSuggestionBinding> itemHolder = (VHolder<ItemSuggestionBinding>) holder;
ItemSuggestionBinding vb = itemHolder.getVb();
String text = data.get(position);
vb.tvSuggestion.setText(text);
vb.relayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(homeItemClickListener!= null){
homeItemClickListener.onClickItemSuggestion(true,text);
}
}
});
vb.imFillIn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(homeItemClickListener!= null){
homeItemClickListener.onClickItemSuggestion(false,text);
}
}
});
}
}

View File

@ -23,4 +23,15 @@ public interface HomeItemClickListener {
default void onClickItemCategoryList(ResponsePlayListInfo data,int musicIndex) {
}
/**
* 点击了搜索建议
* @param isSearch true 直接搜索 false 输入搜索建议文字
* @param data
*/
default void onClickItemSuggestion(boolean isSearch,String data) {
}
}

View File

@ -0,0 +1,34 @@
package com.hi.music.player.javabean.requestbody;
import com.hi.music.player.javabean.requestbody.child.Client;
import com.hi.music.player.javabean.requestbody.child.ContextBody;
import java.io.Serializable;
/**
* 搜索建议接口
*/
public class BodySearch implements Serializable {
private String query = "";
private ContextBody context = new ContextBody();
public String getQuery() {
return query;
}
public void setQuery(String query) {
this.query = query;
}
public ContextBody getContext() {
return context;
}
public void setContext(ContextBody context) {
this.context = context;
}
}

View File

@ -0,0 +1,34 @@
package com.hi.music.player.javabean.requestbody;
import com.hi.music.player.javabean.requestbody.child.Client;
import com.hi.music.player.javabean.requestbody.child.ContextBody;
import java.io.Serializable;
/**
* 搜索建议接口
*/
public class BodySearchSuggestion implements Serializable {
private String input = "";
public String getInput() {
return input;
}
public void setInput(String input) {
this.input = input;
}
private ContextBody context = new ContextBody();
public ContextBody getContext() {
return context;
}
public void setContext(ContextBody context) {
this.context = context;
}
}

View File

@ -0,0 +1,38 @@
package com.hi.music.player.javabean.requestbody.child;
import java.io.Serializable;
import java.util.Locale;
public class Client implements Serializable {
private String clientName = "WEB_REMIX";
//1.20240506.01.00
private String clientVersion = "1.20220918";
private String hl = Locale.getDefault().getLanguage();
private String gl = "US";
private String platform = "DESKTOP";
private String visitorData;
public String getVisitorData() {
return visitorData;
}
public void setVisitorData(String visitorData) {
this.visitorData = visitorData;
}
public void setClientName(String clientName) {
this.clientName = clientName;
}
public void setClientVersion(String clientVersion) {
this.clientVersion = clientVersion;
}
public void setPlatform(String platform) {
this.platform = platform;
}
}

View File

@ -23,37 +23,7 @@ public class ContextBody {
}
public static class Client implements Serializable {
private String clientName = "WEB_REMIX";
//1.20240506.01.00
private String clientVersion = "1.20220918";
private String hl = Locale.getDefault().getLanguage();
private String gl = "US";
private String platform = "DESKTOP";
private String visitorData;
public String getVisitorData() {
return visitorData;
}
public void setVisitorData(String visitorData) {
this.visitorData = visitorData;
}
public void setClientName(String clientName) {
this.clientName = clientName;
}
public void setClientVersion(String clientVersion) {
this.clientVersion = clientVersion;
}
public void setPlatform(String platform) {
this.platform = platform;
}
}
public static class ThirdParty{
//https://www.youtube.com/watch?v=UqyT8IEBkvY

View File

@ -323,6 +323,84 @@ public class JsonHelper {
}
public static List<String> ResolveSearchSuggestion(JSONObject jsonObject) {
try {
List<String> stringList = new ArrayList<>();
JSONArray contents = jsonObject.getJSONArray("contents");
for (int i = 0; i < contents.length(); i++) {
JSONArray jsonArray = contents.getJSONObject(i)
.getJSONObject("searchSuggestionsSectionRenderer")
.getJSONArray("contents");
for (int j = 0; j < jsonArray.length(); j++) {
JSONObject searchSuggestionRenderer = jsonArray.getJSONObject(j)
.optJSONObject("searchSuggestionRenderer");
if (searchSuggestionRenderer != null) {
String string = searchSuggestionRenderer
.getJSONObject("navigationEndpoint")
.getJSONObject("searchEndpoint")
.getString("query");
stringList.add(string);
}
}
}
return stringList;
} catch (JSONException e) {
CommonUtils.LogErrorMsg("--" + e.getMessage());
return null;
}
}
/**
* 解析搜索结果
*
* @param jsonObject
*/
public static void ResolveSearchResult(JSONObject jsonObject) {
try {
JSONArray jsonArray = jsonObject.getJSONObject("contents")
.getJSONObject("tabbedSearchResultsRenderer")
.getJSONArray("tabs")
.getJSONObject(0)
.getJSONObject("tabRenderer")
.getJSONObject("content")
.getJSONObject("sectionListRenderer")
.getJSONArray("contents");
for(int i =0;i<jsonArray.length();i++){
if(i ==0){
//最佳结果
}else {
}
}
} catch (JSONException e) {
CommonUtils.LogErrorMsg("--" + e.getMessage());
}
}
private static ResponsePlayListInfo getCommonCategoryList(JSONObject musicResponsiveListItemRenderer) throws JSONException {
JSONObject jsonText = musicResponsiveListItemRenderer.getJSONArray("fixedColumns")
.getJSONObject(0)
@ -603,12 +681,12 @@ public class JsonHelper {
int index = 0;
if (maxBig) {
index = length - 1;
}else if(length>2){
} else if (length > 2) {
index = length - 2;
}
String pngUrl = jsonArray.getJSONObject(index).getString("url");
CommonUtils.LogMsg("----------取封面index="+index);
CommonUtils.LogMsg("----------取封面index=" + index);
return pngUrl;
} catch (JSONException exception) {

View File

@ -51,7 +51,24 @@ public interface MusicApi {
//首页分类项下的播放列表子页面 (类型1单曲合集2专辑3音乐视频合集 不同类型返回数据结构有区别)
@POST("youtubei/v1/browse?prettyPrint=false")
@Headers("X-Goog-Api-Key:AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8")
@Headers({"X-Goog-Api-Key:AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8"})
Observable<ResponseBody> getCategoryList(@Body RequestBody requestBody);
@POST("youtubei/v1/music/get_search_suggestions?prettyPrint=false")
@Headers({"X-Goog-Api-Key:AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8",
"X-Goog-FieldMask:contents.searchSuggestionsSectionRenderer.contents.searchSuggestionRenderer.navigationEndpoint.searchEndpoint.query"})
Observable<ResponseBody> getSearchSuggestion(@Body RequestBody requestBody);
// "X-Goog-FieldMask:contents.tabbedSearchResultsRenderer.tabs.tabRenderer.content.sectionListRenderer.contents.musicShelfRenderer(continuations,contents.musicResponsiveListItemRenderer(flexColumns,fixedColumns,thumbnail,navigationEndpoint))"
@POST("youtubei/v1/search?prettyPrint=false")
@Headers({"X-Goog-Api-Key:AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8"})
Observable<ResponseBody> getSearch(@Body RequestBody requestBody);
}

View File

@ -5,6 +5,9 @@ import com.hi.music.player.api.RequestListener;
import com.hi.music.player.javabean.requestbody.BodyHome;
import com.hi.music.player.javabean.requestbody.BodyPlay;
import com.hi.music.player.javabean.requestbody.BodyPlayUrl;
import com.hi.music.player.javabean.requestbody.BodySearch;
import com.hi.music.player.javabean.requestbody.BodySearchSuggestion;
import com.hi.music.player.javabean.requestbody.child.Client;
import com.hi.music.player.javabean.requestbody.child.ContextBody;
import java.util.HashMap;
@ -32,8 +35,8 @@ public class RetrofitManager {
public MediaType JSON = MediaType.get("application/json; charset=utf-8");
private MusicApi musicApi;
private String header1="playlistPanelVideoRenderer(title,navigationEndpoint,longBylineText,shortBylineText,thumbnail,lengthText)";
private String header ="contents.singleColumnMusicWatchNextResultsRenderer.tabbedRenderer.watchNextTabbedResultsRenderer.tabs.tabRenderer.content.musicQueueRenderer.content.playlistPanelRenderer(continuations,contents(automixPreviewVideoRenderer,"+header1+"))";
private String header1 = "playlistPanelVideoRenderer(title,navigationEndpoint,longBylineText,shortBylineText,thumbnail,lengthText)";
private String header = "contents.singleColumnMusicWatchNextResultsRenderer.tabbedRenderer.watchNextTabbedResultsRenderer.tabs.tabRenderer.content.musicQueueRenderer.content.playlistPanelRenderer(continuations,contents(automixPreviewVideoRenderer," + header1 + "))";
private RetrofitManager() {
@ -89,7 +92,7 @@ public class RetrofitManager {
.subscribe(new ObserverWrapper<ResponseBody>(requestListener));
}
public void getHomeMoreData(String continuation,String itct,String visitorData,RequestListener<ResponseBody> requestListener) {
public void getHomeMoreData(String continuation, String itct, String visitorData, RequestListener<ResponseBody> requestListener) {
BodyHome bodyHome = new BodyHome();
bodyHome.getContext().getClient().setVisitorData(visitorData);
Gson gson = new Gson();
@ -97,12 +100,12 @@ public class RetrofitManager {
RequestBody requestBody = RequestBody.Companion.create(s, JSON);
HashMap<String, String> stringHashMap = new HashMap<>();
stringHashMap.put("ctoken",continuation);
stringHashMap.put("continuation",continuation);
stringHashMap.put("type","next");
stringHashMap.put("itct",itct);
stringHashMap.put("prettyPrint","false");
musicApi.getHomeMoreData(continuation,continuation,"next",itct,false,requestBody)
stringHashMap.put("ctoken", continuation);
stringHashMap.put("continuation", continuation);
stringHashMap.put("type", "next");
stringHashMap.put("itct", itct);
stringHashMap.put("prettyPrint", "false");
musicApi.getHomeMoreData(continuation, continuation, "next", itct, false, requestBody)
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
@ -119,7 +122,7 @@ public class RetrofitManager {
Gson gson = new Gson();
String s = gson.toJson(bodyPlay);
RequestBody requestBody = RequestBody.Companion.create(s, JSON);
musicApi.getMusicPlayPage(header,requestBody)
musicApi.getMusicPlayPage(header, requestBody)
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
@ -127,15 +130,14 @@ public class RetrofitManager {
}
public void getPlayUrl(String videoId,RequestListener<ResponseBody> requestListener) {
public void getPlayUrl(String videoId, RequestListener<ResponseBody> requestListener) {
BodyPlayUrl bodyPlay = new BodyPlayUrl();
bodyPlay.setVideoId(videoId);
ContextBody.Client client = bodyPlay.getContext().getClient();
Client client = bodyPlay.getContext().getClient();
client.setClientName("ANDROID_MUSIC");
client.setClientVersion("5.28.1");
client.setPlatform("MOBILE");
bodyPlay.getContext().getThirdParty().setEmbedUrl("https://www.youtube.com/watch?v="+videoId);
bodyPlay.getContext().getThirdParty().setEmbedUrl("https://www.youtube.com/watch?v=" + videoId);
Gson gson = new Gson();
@ -149,13 +151,12 @@ public class RetrofitManager {
}
public void getCategoryList(String browserId,RequestListener<ResponseBody> requestListener) {
public void getCategoryList(String browserId, RequestListener<ResponseBody> requestListener) {
BodyHome bodyHome = new BodyHome();
bodyHome.setBrowseId(browserId);
// bodyHome.setBrowseId("VLPLI-asvmHZWNo_xjMMfD_v2O2lTyCdrjaK");
ContextBody.Client client = bodyHome.getContext().getClient();
Client client = bodyHome.getContext().getClient();
client.setClientVersion("1.20240506.01.00");
@ -164,13 +165,44 @@ public class RetrofitManager {
RequestBody requestBody = RequestBody.Companion.create(s, JSON);
musicApi.getCategoryList(requestBody)
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new ObserverWrapper<ResponseBody>(requestListener));
.subscribe(new ObserverWrapper<>(requestListener));
}
public void getSearchSuggestion(String input, RequestListener<ResponseBody> requestListener) {
BodySearchSuggestion body = new BodySearchSuggestion();
body.setInput(input);
Client client = body.getContext().getClient();
client.setClientVersion("1.20240506.01.00");
Gson gson = new Gson();
String s = gson.toJson(body);
RequestBody requestBody = RequestBody.Companion.create(s, JSON);
musicApi.getSearchSuggestion(requestBody)
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new ObserverWrapper<>(requestListener));
}
public void getSearch(String query, RequestListener<ResponseBody> requestListener) {
BodySearch body = new BodySearch();
body.setQuery(query);
Client client = body.getContext().getClient();
client.setClientVersion("1.20240506.01.00");
Gson gson = new Gson();
String s = gson.toJson(body);
RequestBody requestBody = RequestBody.Companion.create(s, JSON);
musicApi.getSearch(requestBody)
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new ObserverWrapper<>(requestListener));
}
}

View File

@ -1,11 +1,29 @@
package com.hi.music.player.ui.fragmnt;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.TextView;
import androidx.lifecycle.Observer;
import androidx.recyclerview.widget.LinearLayoutManager;
import com.hi.music.player.adapter.AdapterSuggestion;
import com.hi.music.player.api.HomeItemClickListener;
import com.hi.music.player.databinding.FragmentSearchBinding;
import com.hi.music.player.helper.CommonUtils;
import com.hi.music.player.ui.fragmnt.viewmodel.VMSearch;
import java.util.List;
public class SearchFragment extends BaseFragment<FragmentSearchBinding> {
public class SearchFragment extends BaseFragment<FragmentSearchBinding> implements HomeItemClickListener, View.OnClickListener {
private VMSearch vmSearch;
@Override
protected FragmentSearchBinding getFragmentVb() {
return FragmentSearchBinding.inflate(getLayoutInflater());
@ -13,6 +31,82 @@ public class SearchFragment extends BaseFragment<FragmentSearchBinding> {
@Override
protected void initView() {
// Vb.etSearch.requestFocus();
vmSearch = getFragmentScopeViewModel(VMSearch.class);
Vb.etSearch.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
String input = s.toString();
if (!input.isEmpty()) {
vmSearch.getSuggestion(input);
}
}
});
Vb.etSearch.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
String s = v.getText().toString();
CommonUtils.LogMsg("------------v=" + s);
startQuery(s);
return true;
} else
return false;
}
});
initSuggestionRecycler();
}
private void initSuggestionRecycler() {
AdapterSuggestion adapterSuggestion = new AdapterSuggestion();
Vb.recyclerSuggestion.setLayoutManager(new LinearLayoutManager(requireContext()));
Vb.recyclerSuggestion.setAdapter(adapterSuggestion);
adapterSuggestion.setHomeItemClickListener(this);
Vb.imCancel.setOnClickListener(this);
vmSearch.suggestion.observe(getViewLifecycleOwner(), new Observer<List<String>>() {
@Override
public void onChanged(List<String> strings) {
if (strings != null) {
adapterSuggestion.setData(strings);
CommonUtils.LogMsg("------------更新=");
}
}
});
}
private void startQuery(String query){
Vb.recyclerSuggestion.setVisibility(View.GONE);
Vb.recyclerResult.setVisibility(View.VISIBLE);
vmSearch.getSearchResult(query);
}
@Override
public void onClickItemSuggestion(boolean isSearch, String data) {
if (isSearch) {
startQuery(data);
} else {
Vb.etSearch.setText(data);
}
}
@Override
public void onClick(View v) {
if (v.equals(Vb.imCancel)) {
Vb.etSearch.setText("");
}
}
}

View File

@ -0,0 +1,66 @@
package com.hi.music.player.ui.fragmnt.viewmodel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import com.hi.music.player.api.RequestListener;
import com.hi.music.player.helper.CommonUtils;
import com.hi.music.player.javabean.response.ResponseHome;
import com.hi.music.player.network.JsonHelper;
import com.hi.music.player.network.RetrofitManager;
import org.json.JSONObject;
import java.util.List;
import okhttp3.ResponseBody;
public class VMSearch extends ViewModel {
private MutableLiveData<List<String>> _suggestion = new MutableLiveData<List<String>>();
public LiveData<List<String>> suggestion = _suggestion;
public void getSuggestion(String input) {
RetrofitManager.getInstance().getSearchSuggestion(input,new RequestListener<ResponseBody>() {
@Override
public void onFail(String errorMsg) {
_suggestion.setValue(null);
}
@Override
public void onSuccess(JSONObject data) {
if (data != null) {
List<String> stringList = JsonHelper.ResolveSearchSuggestion(data);
CommonUtils.LogMsg("------------onSuccess= stringList="+stringList );
_suggestion.setValue(stringList);
} else {
_suggestion.setValue(null);
}
}
});
}
public void getSearchResult(String query) {
RetrofitManager.getInstance().getSearch(query, new RequestListener<ResponseBody>() {
@Override
public void onFail(String errorMsg) {
// _data.setValue(null);
}
@Override
public void onSuccess(JSONObject data) {
if (data != null) {
JsonHelper.ResolveSearchResult(data);
// _data.setValue(responseHome);
} else {
// _data.setValue(null);
}
}
});
}
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="20dp"/>
<solid android:color="@color/black"/>
</shape>

View File

@ -0,0 +1,20 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M7,7L17,17"
android:strokeLineJoin="round"
android:strokeWidth="1.5"
android:fillColor="#00000000"
android:strokeColor="#ffffff"
android:strokeLineCap="round"/>
<path
android:pathData="M7,17L17,7"
android:strokeLineJoin="round"
android:strokeWidth="1.5"
android:fillColor="#00000000"
android:strokeColor="#ffffff"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="@color/white"
android:pathData="M820.3,782.6L288.5,250.9h444.4c14.6,0 26.4,-11.8 26.4,-26.4s-11.8,-26.4 -26.4,-26.4H233.1c-19.4,0 -35.1,15.7 -35.1,35.1v499.8c0,14.6 11.8,26.4 26.4,26.4s26.4,-11.8 26.4,-26.4V287.7l532.2,532.2c5.1,5.1 11.9,7.7 18.6,7.7s13.5,-2.6 18.6,-7.7c10.4,-10.3 10.4,-27 0.2,-37.3z"/>
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M11.12,20.72C5.84,20.72 1.52,16.4 1.52,11.12C1.52,5.84 5.84,1.52 11.12,1.52C16.399,1.52 20.719,5.84 20.719,11.12C20.719,16.4 16.399,20.72 11.12,20.72ZM11.12,19.12C15.519,19.12 19.119,15.52 19.119,11.12C19.119,6.72 15.519,3.12 11.12,3.12C6.72,3.12 3.12,6.72 3.12,11.12C3.12,15.52 6.72,19.12 11.12,19.12ZM17.039,20.4C16.719,20.08 16.799,19.52 17.119,19.28C17.44,19.04 18,19.04 18.24,19.36L19.76,21.2C20.08,21.52 20,22.08 19.68,22.32C19.36,22.56 18.799,22.56 18.559,22.24L17.039,20.4Z"
android:fillColor="#ffffff"
android:fillAlpha="0.85"/>
</vector>

View File

@ -1,14 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/default_play_list_color"
tools:context=".ui.fragmnt.SearchFragment">
<!-- TODO: Update blank fragment layout -->
<TextView
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/hello_blank_fragment" />
android:layout_height="50dp"
android:layout_marginStart="22dp"
android:layout_marginEnd="22dp"
android:layout_marginTop="16dp"
android:id="@+id/layout_et"
android:background="@drawable/bg_search" >
</FrameLayout>
<EditText
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@null"
android:layout_toStartOf="@id/im_cancel"
android:textColorHint="@color/seek_bg_color"
android:paddingStart="20dp"
android:focusable="true"
android:focusableInTouchMode="true"
android:id="@+id/et_search"
android:textColor="@color/white"
android:maxLines="1"
android:inputType="text"
android:imeOptions="actionSearch"
android:hint="@string/search_hint"/>
<ImageView
android:id="@+id/im_cancel"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
android:paddingStart="13dp"
android:paddingEnd="13dp"
android:src="@drawable/icon_cancel"/>
</RelativeLayout>
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/layout_et"
android:layout_alignStart="@id/layout_et"
android:layout_alignEnd="@id/layout_et"
android:id="@+id/recycler_suggestion"/>
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/layout_et"
android:layout_alignStart="@id/layout_et"
android:layout_alignEnd="@id/layout_et"
android:visibility="gone"
android:id="@+id/recycler_result"/>
</RelativeLayout>

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:id="@+id/relayout"
android:layout_height="50dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:id="@+id/im"
android:src="@drawable/icon_search" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:layout_toEndOf="@id/im"
android:layout_marginStart="25dp"
android:text="@string/app_name"
android:maxLines="1"
android:id="@+id/tv_suggestion"
android:ellipsize="end"
android:layout_toStartOf="@id/im_fill_in"
android:layout_centerVertical="true"
android:textSize="16sp" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:id="@+id/im_fill_in"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:layout_alignParentEnd="true"
android:layout_centerInParent="true"
android:src="@drawable/icon_fill_in" />
</RelativeLayout>

View File

@ -24,4 +24,5 @@
<string name="play_next">Play next</string>
<string name="retry">Try again</string>
<string name="An_error_occurred">An error occurred</string>
<string name="search_hint">Search songs, artists…</string>
</resources>