我最近开发了一个显示旅游景点的 Android 应用程序。传递给 RecyclerView.Adapter 的 List 已经按目的地和首都之间的距离排序。例如,
- 悉尼|悉尼歌剧院|174m
- 悉尼|The Strand Arcade|391m
- 悉尼|情人港|1.5km
- 悉尼|悉尼海港大桥|1.7公里
- 悉尼|邦迪海滩|6.5公里
- 悉尼|蓝山|84.6km
- 悉尼|珍罗兰石窟|104.4km
- 悉尼|猎人谷|117.2km
- 悉尼|海姆斯海滩|144.5公里
- 悉尼|赏鲸|224.6公里
- 悉尼|塔龙加西部平原动物园|331.8km
- 悉尼|Thredbo - 滑雪胜地|393.7km
问题是当我使用不同的移动设备或模拟器时,项目以不同的顺序显示的应用程序。例如,
像素 XL
连结 5X
我一直在寻找解决方案几天,但没有成功。有人会在这里帮助我,将不胜感激。下面是我的布局和适配器。
implementation 'com.android.support:recyclerview-v7:27.1.0'
public class AttractionListFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
attractions = populateAttractionData(ausAttractionList, state, mSelectedStateLat);
View view = inflater.inflate(R.layout.fragment_main, container, false);
mAdapter = new AttractionAdapter(getActivity(), attractions);
AttractionsRecyclerView recyclerView = (AttractionsRecyclerView) view.findViewById(android.R.id.list);
recyclerView.setEmptyView(view.findViewById(android.R.id.empty));
recyclerView.setHasFixedSize(true);
recyclerView.setAdapter(mAdapter);
return view;
}
private class AttractionAdapter extends RecyclerView.Adapter<ViewHolder>
implements ItemClickListener {
public List<Attraction> mAttractionList;
private Context mContext;
public AttractionAdapter(Context context, List<Attraction> attractions) {
super();
mContext = context;
mAttractionList = attractions;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(mContext);
View view = inflater.inflate(R.layout.list_row, parent, false);
return new ViewHolder(view, this);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Attraction attraction = mAttractionList.get(position);
holder.mTitleTextView.setText(attraction.name);
holder.mDescriptionTextView.setText(attraction.description);
Glide.with(mContext)
.load(attraction.imageUrl)
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.placeholder(R.drawable.empty_photo)
.override(mImageSize, mImageSize)
.into(holder.mImageView);
holder.mOverlayTextView.setText(attraction.distance);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public int getItemCount() {
return mAttractionList == null ? 0 : mAttractionList.size();
}
@Override
public void onItemClick(View view, int position) {
if (!mItemClicked) {
mItemClicked = true;
View heroView = view.findViewById(android.R.id.icon);
DetailActivity.launch(getActivity(), mAdapter.mAttractionList.get(position).name, heroView);
}
}
}
private static class ViewHolder extends RecyclerView.ViewHolder
implements View.OnClickListener {
TextView mTitleTextView;
TextView mDescriptionTextView;
TextView mOverlayTextView;
ImageView mImageView;
ItemClickListener mItemClickListener;
public ViewHolder(View view, ItemClickListener itemClickListener) {
super(view);
mTitleTextView = (TextView) view.findViewById(android.R.id.text1);
mDescriptionTextView = (TextView) view.findViewById(android.R.id.text2);
mOverlayTextView = (TextView) view.findViewById(R.id.overlaytext);
mImageView = (ImageView) view.findViewById(android.R.id.icon);
mItemClickListener = itemClickListener;
view.setOnClickListener(this);
}
@Override
public void onClick(View v) {
mItemClickListener.onItemClick(v, getAdapterPosition());
}
}
interface ItemClickListener {
void onItemClick(View view, int position);
}
public class AttractionsRecyclerView extends RecyclerView {
private View mEmptyView;
private AdapterDataObserver mDataObserver = new AdapterDataObserver() {
@Override
public void onChanged() {
super.onChanged();
updateEmptyView();
}
};
public AttractionsRecyclerView(Context context) {
super(context);
}
public AttractionsRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AttractionsRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/**
* Designate a view as the empty view. When the backing adapter has no
* data this view will be made visible and the recycler view hidden.
*
*/
public void setEmptyView(View emptyView) {
mEmptyView = emptyView;
}
@Override
public void setAdapter(RecyclerView.Adapter adapter) {
if (getAdapter() != null) {
getAdapter().unregisterAdapterDataObserver(mDataObserver);
}
if (adapter != null) {
adapter.registerAdapterDataObserver(mDataObserver);
}
super.setAdapter(adapter);
updateEmptyView();
}
private void updateEmptyView() {
if (mEmptyView != null && getAdapter() != null) {
boolean showEmptyView = getAdapter().getItemCount() == 0;
mEmptyView.setVisibility(showEmptyView ? VISIBLE : GONE);
setVisibility(showEmptyView ? GONE : VISIBLE);
}
}
}
fragment_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="2dp"
android:orientation="vertical"
tools:context=".ui.AttractionListFragment">
<HorizontalScrollView
android:layout_width="366dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="2dp"
android:layout_marginTop="10dp"
android:layout_marginRight="2dp"
android:layout_marginBottom="10dp"
android:scrollbars="none">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/id_things_to_do"
android:layout_width="150dp"
android:layout_height="120dp"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_margin="2dp"
android:scaleType="center"
android:src="@drawable/vic_attraction_grampian" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/id_things_to_do"
android:gravity="center"
android:text="@string/things_to_do"
android:textSize="15sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/id_one_day_tour"
android:layout_width="150dp"
android:layout_height="120dp"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_margin="2dp"
android:scaleType="centerCrop"
android:src="@drawable/vic_attraction_grampian" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/id_one_day_tour"
android:text="@string/day_trip"
android:gravity="center"
android:textSize="15sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/id_two_day_tour"
android:layout_width="150dp"
android:layout_height="120dp"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_margin="2dp"
android:scaleType="centerCrop"
android:src="@drawable/vic_attraction_grampian" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/id_two_day_tour"
android:gravity="center"
android:text="@string/two_days_tour"
android:textSize="15sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/id_three_day_tour"
android:layout_width="150dp"
android:layout_height="120dp"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_margin="2dp"
android:scaleType="centerCrop"
android:src="@drawable/vic_attraction_grampian" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/id_three_day_tour"
android:gravity="center"
android:text="@string/three_days_tour"
android:textSize="15sp" />
</LinearLayout>
</LinearLayout>
</HorizontalScrollView>
<com.kplau.attractionsinAustralia.ui.AttractionsRecyclerView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
app:layoutManager="GridLayoutManager"
app:spanCount="@integer/list_columns" />
<TextView
android:id="@android:id/empty"
android:text="@string/empty_list"
style="?android:attr/textAppearanceMedium"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center" />
</LinearLayout>
list_row.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:foreground="?attr/selectableItemBackground"
android:layout_height="@dimen/image_size">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/gallery_list">
<ImageView
android:id="@android:id/icon"
android:layout_width="@dimen/image_size"
android:layout_height="match_parent"
android:src="@drawable/empty_photo"
android:scaleType="centerCrop"
android:transitionName="image" />
<TextView
android:id="@+id/overlaytext"
android:layout_width="@dimen/image_size"
android:layout_height="wrap_content"
android:layout_alignBottom="@android:id/icon"
android:gravity="center"
android:padding="@dimen/tiny_margin"
style="?android:textAppearanceSmallInverse"
android:background="@color/text_background"
tools:text="Overlay"/>
<TextView
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toEndOf="@android:id/icon"
android:paddingTop="@dimen/small_margin"
android:paddingLeft="@dimen/small_margin"
android:paddingRight="@dimen/small_margin"
android:maxLines="1"
android:ellipsize="end"
android:textSize="16sp"
style="@style/TextAppearance.AppCompat.Body1"
tools:text="Title 1" />
<TextView
android:id="@android:id/text2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toEndOf="@android:id/icon"
android:layout_below="@android:id/text1"
android:padding="@dimen/small_margin"
android:ellipsize="end"
android:maxLines="4"
android:textSize="14sp"/>
</RelativeLayout>
</FrameLayout>
Main Activity
download the data from website and store in the later use by the populateAttractionData method.
private class DownloadTask extends AsyncTask<String, Void, Object> {
protected Object doInBackground(String... args) {
//System.out.println("Debug : MainActivity.DownloadTask.searchUrl[Start]");
//String searchUrl = "http://www.flipjob.com.au/export_attractions.csv";
HttpURLConnection urlConnection = null;
int counter = 0;
try {
//System.out.println("Debug : MainActivity.DownloadTask.urlConnection.searchUrl[" + searchUrl + "]");
URL url = new URL(SEARCH_URL);
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
int statusCode = urlConnection.getResponseCode();
//System.out.println("Debug : MainActivity.DownloadTask.urlConnection.statusCode[" + statusCode + "]");
// title:string shortdescr:string longdescr:string image:string latitude:float longitude:float state:string city:string
if (statusCode == 200) {
BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));;
String line = null;
while ((line = in.readLine()) != null) {
if (line==null && line.length()< 100) {
//Log.d(TAG, "Debug : PaidJobActivity.DownloadTask.searchUrl[line == null]");
break;
} else {
counter++;
//System.out.println("Debug : MainActivity.DownloadTask.line[" + line + "] | counter[" + counter + "]");
String[] row = line.split("\\|");
int id = Integer.parseInt(row[0].toString());
String title = row[1].toString();
String shortdescr = row[2].toString();
String longdescr = row[3].toString();
String image = row[4].toString();
//System.out.println("Debug : MainActivity.AustraliaAttraction[" + counter + ":" + row[5].toString() + "|" + row[6].toString() + "]");
float latitude = Float.parseFloat(row[5].toString());
float longitude = Float.parseFloat(row[6].toString());
String state = row[7].toString();
String city = row[8].toString();
String website = row[9].toString();
String attractionType = row[10].toString();
String titleChinese = row[11].toString();
String shortdescrChinese = row[12].toString();
String longdescrChinese = row[13].toString();
LatLng attractionLoc = new LatLng(latitude, longitude);
LatLng stateLat = Utils.getSelectedStateLocation(state);
double distanceBetweenCapitalCity = Utils.calculateDistanceBetweenCapitalCity(stateLat, attractionLoc);
//System.out.println("Debug : MainActivity.AustraliaAttraction[" + counter + ":" + title + "|" + shortdescr + "|" + longdescr + "|" + image + "|" + latitude + "|" + longitude + "|" + state + "|" + city);
australiaAttractionList.add(new AustraliaAttraction(id, title, titleChinese, website, attractionType, shortdescr, shortdescrChinese, longdescr, longdescrChinese, image, latitude, longitude, distanceBetweenCapitalCity, state, city));
//System.out.println("Debug : MainActivity.AustraliaAttraction = add[" + counter + ":" + title + "|" + image + "|" + latitude + "|" + longitude + "|" + distanceBetweenCapitalCity + "|" + state + "|" + city);
//Log.d(TAG, "Debug : MainActivity.after adding a job to currentJobVacancyList");
}
}
in.close();
}
} catch (MalformedURLException e) {
System.out.println("Debug : MainActivity: failed downloading file using http RESTFUL - MalformedURLException due to[" + e.getMessage() + "]");
e.printStackTrace();
}
catch (IOException e) {
System.out.println("Debug : MainActivity : failed downloading file using http RESTFUL - IOException due to[" + e.getMessage() + "]");
e.printStackTrace();
}
// Sort australiaAttractionList by distanceBetweenCaptial
Collections.sort(australiaAttractionList, new Comparator<AustraliaAttraction>() {
public int compare(AustraliaAttraction o1, AustraliaAttraction o2) {
return Double.compare(o1.getDistanceBetweenCaptialCity(), o2.getDistanceBetweenCaptialCity());
}
});
//System.out.println("Debug : MainActivity.DownloadTask.searchUrl[End]");
// for (AustraliaAttraction australiaAttraction : australiaAttractionList) {
// System.out.println("Debug : MainActivity.title[" + australiaAttraction.getTitle() + "|" + australiaAttraction.getDistanceBetweenCaptialCity());
// }
return null;
}
populateAttractionData
=======================
public static List<Attraction>
populateAttractionData(ArrayList<AustraliaAttraction> ausAttractionList, String state, LatLng selectedStateLat ) {
//System.out.println("Debug : populateAttractionData.state[" + state + "]");
List<Attraction> attractionList = new ArrayList<Attraction>();
Attraction attraction;
for(AustraliaAttraction australiaAttraction : ausAttractionList) {
if (state.equalsIgnoreCase(australiaAttraction.getState())) {
//System.out.println("Kam : populateAttractionData.state[" + state + "] | getTitle[" + australiaAttraction.getTitle() + "]|distanceBetweenCaptialCity[" + australiaAttraction.getDistanceBetweenCaptialCity() + "]");
LatLng attractionlatLng = new LatLng(australiaAttraction.getLatitude(), australiaAttraction.getLongitude());
String distanceBetweenCaptialCityStr = Utils.formatDistanceBetweenCaptialCity(australiaAttraction.getDistanceBetweenCaptialCity());
final String imageWithFullPath = "http://flipjob.com.au/images/" + state.toLowerCase() + "/" + australiaAttraction.getImageUrl();
Attraction myattraction = new Attraction(
australiaAttraction.getAttractionId(),
australiaAttraction.getTitle(),
australiaAttraction.getWebsite(),
australiaAttraction.getTitleChinese(),
australiaAttraction.getShortDesc(),
australiaAttraction.getShortDescChinese(),
australiaAttraction.getLongDesc(),
australiaAttraction.getLongDescChinese(),
Uri.parse(imageWithFullPath),
Uri.parse(imageWithFullPath),
attractionlatLng,
distanceBetweenCaptialCityStr,
australiaAttraction.getCity());
attractionList.add(myattraction);
}
}
// for(Attraction debugAttraction : attractionList){
// System.out.println("Debug : TouristAttractions.populateAttractionData[" + debugAttraction.name + "|" + debugAttraction.distance + "]");
// }