在我提出问题之前,这里是对代码的引用:
项目代码:https://github.com/paimonsoror/RX8Club.com-Forum-Application
UI框架代码(幻灯片导航,右侧有内容):
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/drawer_layout"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<FrameLayout
android:id="@+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<ListView android:id="@+id/left_drawer"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:choiceMode="singleChoice"
android:divider="@android:color/transparent"
android:dividerHeight="0dp"
android:background="#111"/>
</android.support.v4.widget.DrawerLayout>
内容的列表视图:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:id="@+id/content_parent">
<TextView
android:id="@+id/mainlisttitle"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/threadTitlePlaceholder"
android:background="#0000ff"
android:textSize="10dip"
android:textColor="#ffffff"
android:textStyle="bold"
android:gravity="center"
android:visibility="gone"
/>
<!-- <com.normalexception.forum.rx8club.view.PTRListView -->
<ListView
android:id="@+id/mainlistview"
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:divider="#b5b5b5"
android:dividerHeight="1dp"
android:fadingEdgeLength="0dp"
android:background="@android:color/darker_gray" />
</LinearLayout>
项目的视图对象:
import java.util.List;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.text.Html;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebSettings;
import android.webkit.WebSettings.LayoutAlgorithm;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.normalexception.app.rx8club.Log;
import com.normalexception.app.rx8club.MainApplication;
import com.normalexception.app.rx8club.R;
import com.normalexception.app.rx8club.WebUrls;
import com.normalexception.app.rx8club.cache.Cache;
import com.normalexception.app.rx8club.dialog.ReportPostDialog;
import com.normalexception.app.rx8club.fragment.FragmentUtils;
import com.normalexception.app.rx8club.fragment.pm.NewPrivateMessageFragment;
import com.normalexception.app.rx8club.fragment.thread.EditPostFragment;
import com.normalexception.app.rx8club.fragment.thread.ThreadFragment;
import com.normalexception.app.rx8club.handler.AvatarLoader;
import com.normalexception.app.rx8club.html.LoginFactory;
import com.normalexception.app.rx8club.preferences.PreferenceHelper;
import com.normalexception.app.rx8club.utils.Utils;
public class PostView extends RelativeLayout {
private AvatarLoader imageLoader;
private TextView username;
private TextView userTitle;
private TextView userPosts;
private TextView userJoin;
private TextView postDate;
private TextView likeText;
private TextView postBox;
private ImageView reportbutton;
private ImageView linkbutton;
private ImageView downButton;
private ImageView avatar;
private ImageView quoteButton;
private ImageView editButton;
private ImageView pmButton;
private ImageView deleteButton;
private WebView postText;
private Logger TAG = LogManager.getLogger(this.getClass());
public PostView(Context context) {
super(context);
LayoutInflater.from(context).inflate(R.layout.view_newreply_children, this, true);
imageLoader=new AvatarLoader(context);
setupChildren();
}
public PostView(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater.from(context).inflate(R.layout.view_newreply_children, this, true);
imageLoader=new AvatarLoader(context);
setupChildren();
}
public PostView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
LayoutInflater.from(context).inflate(R.layout.view_newreply_children, this, true);
imageLoader=new AvatarLoader(context);
setupChildren();
}
/**
* Setup the children we contain in this view
*/
private void setupChildren() {
username = (TextView) findViewById(R.id.nr_username);
userTitle = (TextView) findViewById(R.id.nr_userTitle);
userPosts = (TextView) findViewById(R.id.nr_userPosts);
userJoin = (TextView) findViewById(R.id.nr_userJoin);
postDate = (TextView) findViewById(R.id.nr_postDate);
likeText = (TextView) findViewById(R.id.nr_likeText);
postBox = (TextView) findViewById(R.id.postBox);
reportbutton = (ImageView) findViewById(R.id.nr_reportbutton);
linkbutton = (ImageView) findViewById(R.id.nr_linkbutton);
avatar = (ImageView) findViewById(R.id.nr_image);
downButton = (ImageView) findViewById(R.id.nr_downButton);
quoteButton = (ImageView) findViewById(R.id.nr_quoteButton);
editButton = (ImageView) findViewById(R.id.nr_editButton);
pmButton = (ImageView) findViewById(R.id.nr_pmButton);
deleteButton = (ImageView) findViewById(R.id.nr_deleteButton);
postText = (WebView) findViewById(R.id.nr_postText);
}
/**
* Inflate the view, this technically only gets called the first time the
* view is accessed
* @param parent The parent of the view
* @return An inflated object
*/
public static PostView inflate(ViewGroup parent) {
PostView itemView = (PostView)LayoutInflater.from(parent.getContext())
.inflate(R.layout.view_newreply, parent, false);
return itemView;
}
/**
* Setup our view here. After the view has been inflated and all of the
* view objects have been initialized, we can inflate our view here
* @param post The model we are going to use to populate the view
* @param position Get the position of this view on the window
* @param listener The listener object to attach to the view
*/
public void setPost(final PostModel post, final int position, final OnClickListener listener) {
username.setText(post.getUserName());
userTitle.setText(post.getUserTitle());
userPosts.setText(post.getUserPostCount());
userJoin.setText(post.getJoinDate());
postDate.setText(post.getPostDate());
reportbutton.setVisibility(View.VISIBLE);
if(PreferenceHelper.isShowLikes(getContext())) {
if(post.getLikes().size() > 0) {
String delim = "", likes = "Liked by: ";
for(String like : post.getLikes()) {
likes += delim + like;
delim = ", ";
}
likeText.setText(likes);
} else {
likeText.setVisibility(View.GONE);
}
} else {
likeText.setVisibility(View.GONE);
}
// Lets make sure we remove any font formatting that was done within
// the text
String trimmedPost =
post.getUserPost().replaceAll("(?i)<(/*)font(.*?)>", "");
// Show attachments if the preference allows it
if(PreferenceHelper.isShowAttachments(getContext()))
trimmedPost = appendAttachments(trimmedPost, post.getAttachments());
// Show signatures if the preference allows it
if(PreferenceHelper.isShowSignatures(getContext()) && post.getUserSignature() != null)
trimmedPost = appendSignature(trimmedPost, post.getUserSignature());
// Set html Font color
trimmedPost = Utils.postFormatter(trimmedPost, getContext());
postText.setBackgroundColor(Color.DKGRAY);
postText.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
postText.getSettings().setAppCachePath(
Cache.getExternalCacheDir(getContext()).getAbsolutePath());
postText.getSettings().setAllowFileAccess(false);
postText.getSettings().setAppCacheEnabled(true);
postText.getSettings().setJavaScriptEnabled(false);
postText.getSettings().setSupportZoom(false);
postText.getSettings().setSupportMultipleWindows(false);
postText.getSettings().setUserAgentString(WebUrls.USER_AGENT);
postText.getSettings().setDatabaseEnabled(false);
postText.getSettings().setDomStorageEnabled(false);
postText.getSettings().setLayoutAlgorithm(LayoutAlgorithm.SINGLE_COLUMN);
postText.setOnTouchListener(new View.OnTouchListener() {
@SuppressLint("ClickableViewAccessibility")
public boolean onTouch(View v, MotionEvent event) {
return (event.getAction() == MotionEvent.ACTION_MOVE);
}
});
postText.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// Check if the URL for the site, and if it is a thread or a category
Log.d(TAG, "User Clicked Embedded url");
boolean isThread = false;
if(url.contains("rx8club.com")) {
isThread = url.matches(".*\\-\\d+\\/$");
Log.d(TAG, String.format("The Link (%s) is %sa thread", url, (isThread)? "" : "NOT "));
Bundle args = new Bundle();
args.putString("link", url);
if(isThread) {
FragmentUtils.fragmentTransaction(
(FragmentActivity)view.getContext(),
ThreadFragment.newInstance(),
false, true, args);
return true;
}
}
// Otherwise, the link is not for a page on my site, so launch another Activity that handles URLs
Intent intent =
new Intent(Intent.ACTION_VIEW, Uri.parse(url));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
MainApplication.getAppContext().startActivity(intent);
return true;
}
});
postText.loadDataWithBaseURL(WebUrls.rootUrl, trimmedPost, "text/html", "utf-8", "");
// Load up the avatar of hte user, but remember to remove
// the dateline at the end of the file so that we aren't
// creating multiple images for a user. The image still
// gets returned without a date
if(PreferenceHelper.isShowAvatars(getContext())) {
String nodate_avatar =
post.getUserImageUrl().indexOf('?') == -1?
post.getUserImageUrl() :
post.getUserImageUrl().substring(0, post.getUserImageUrl().indexOf('?'));
if(!nodate_avatar.isEmpty()) {
imageLoader.DisplayImage(nodate_avatar, avatar);
} else {
avatar.setImageResource(R.drawable.rotor_icon);
}
}
// Display the right items if the user is logged in
setUserIcons(this, post.isLoggedInUser());
downButton
.setOnClickListener(listener);
// Set click listeners if we are logged in, hide the buttons
// if we are not logged in
if(LoginFactory.getInstance().isGuestMode()) {
quoteButton.setVisibility(View.GONE);
editButton.setVisibility(View.GONE);
pmButton.setVisibility(View.GONE);
deleteButton.setVisibility(View.GONE);
reportbutton.setVisibility(View.GONE);
} else {
quoteButton
.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
Log.d(TAG, "Quote Clicked");
String txt = Html.fromHtml(post.getUserPost()).toString();
String finalText = String.format("[quote=%s]%s[/quote]",
post.getUserName(), txt);
postBox.setText(finalText);
postBox.requestFocus();
}
});
editButton
.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
Log.d(TAG, "Edit Clicked");
// Create new fragment and transaction
Bundle args = new Bundle();
args.putString("postid", post.getPostId());
args.putString("securitytoken", post.getToken());
Fragment newFragment = new EditPostFragment();
FragmentUtils.fragmentTransaction((FragmentActivity)getContext(),
newFragment, true, true, args);
}
});
reportbutton
.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
Log.d(TAG, "Report Clicked");
new ReportPostDialog(getContext(),
post.getToken(), post.getPostId()).show();
}
});
linkbutton
.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
Log.d(TAG, "Link Clicked");
ClipboardManager clipboard =
(android.content.ClipboardManager)
getContext().getSystemService(Context.CLIPBOARD_SERVICE);
android.content.ClipData clip =
android.content.ClipData.newPlainText("thread link", post.getRootThreadUrl());
clipboard.setPrimaryClip(clip);
Toast.makeText(getContext(),
"Thread Link Copied To Clipboard",
Toast.LENGTH_LONG).show();
}
});
pmButton
.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
Log.d(TAG, "PM Clicked");
// Create new fragment and transaction
Bundle args = new Bundle();
args.putString("user", post.getUserName());
Fragment newFragment = new NewPrivateMessageFragment();
FragmentUtils.fragmentTransaction((FragmentActivity)getContext(),
newFragment, false, true, args);
}
});
final boolean isFirstPost = (position == 0);
deleteButton
.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
DialogInterface.OnClickListener dialogClickListener =
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which){
case DialogInterface.BUTTON_POSITIVE:
// Create new fragment and transaction
Bundle args = new Bundle();
args.putString("postid", post.getPostId());
args.putString("securitytoken", post.getToken());
args.putBoolean("delete", true);
args.putBoolean("deleteThread", isFirstPost && post.isLoggedInUser());
Fragment newFragment = new EditPostFragment();
FragmentUtils.fragmentTransaction((FragmentActivity)getContext(),
newFragment, false, true, args);
break;
}
}
};
AlertDialog.Builder builder =
new AlertDialog.Builder(getContext());
builder
.setMessage("Are you sure you want to delete your post?")
.setPositiveButton("Yes", dialogClickListener)
.setNegativeButton("No", dialogClickListener)
.show();
}
});
}
}
/**
* Append the signature to the end of the user's post
* @param trimmedPost The user's post
* @param signature The signature to add
* @return A full user post
*/
private String appendSignature(String trimmedPost, String signature) {
return String.format("%s<br><br>%s", trimmedPost, signature);
}
/**
* Append attachments to the end of this post if they exist
* @param trimmedPost The current post
* @param attachments The attachments of the post
* @return An appended html string
*/
private String appendAttachments(String trimmedPost,
List<String> attachments) {
if(attachments == null || attachments.isEmpty())
return trimmedPost;
// Create an html string for the attachments
String attachString = "";
for(String attachment : attachments)
attachString +=
String.format("<a href=\"%s\"><img class=\"attachment\" src=\"%s\"></a> ",
attachment, attachment);
// Now append to the original text
trimmedPost =
String.format("%s<br><br><b>Attachments:</b><br>%s",
trimmedPost, attachString);
return trimmedPost;
}
/**
* If the post is by the logged in user, make sure that they can see the edit and
* delete buttons
* @param vi The thread view object
* @param isLoggedInUser True if post is by logged user
*/
private void setUserIcons(View vi, boolean isLoggedInUser) {
quoteButton
.setVisibility(View.VISIBLE);
pmButton
.setVisibility(View.VISIBLE);
editButton
.setVisibility(isLoggedInUser? View.VISIBLE : View.GONE);
deleteButton
.setVisibility(isLoggedInUser? View.VISIBLE : View.GONE);
}
}
项目的视图布局:
<?xml version="1.0" encoding="utf-8"?>
<merge
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<!-- ListRow Left sied Thumbnail image -->
<LinearLayout
android:id="@+id/thumbnail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_marginRight="5dip"
android:baselineAligned="false"
android:padding="0dip" >
<ImageView
android:id="@+id/tv_image"
android:layout_width="16dip"
android:layout_height="16dip"
android:adjustViewBounds="false"
android:baselineAlignBottom="false"
android:cropToPadding="false"
android:scaleType="fitXY"
android:src="@drawable/push_pin" />
</LinearLayout>
<!-- Title Of Thread-->
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/thumbnail"
android:layout_marginRight="60dp"
android:layout_toRightOf="@+id/thumbnail"
android:text="@string/tvTitle"
android:textColor="#040404"
android:textSize="12dip"
android:textStyle="bold"
android:typeface="sans" />
<!-- Started By -->
<TextView
android:id="@+id/tv_postUser_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tv_title"
android:layout_marginTop="1dip"
android:layout_toRightOf="@+id/thumbnail"
android:text="@string/tvStarted"
android:textColor="#000000"
android:textSize="10dip" />
<TextView
android:id="@+id/tv_postUser"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tv_title"
android:textColor="#ffffff"
android:textSize="10dip"
android:layout_marginTop="1dip"
android:layout_toRightOf="@+id/tv_postUser_label"
android:text="The Thread Starter" />
<!-- Started By -->
<TextView
android:id="@+id/tv_lastUser_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tv_postUser_label"
android:textColor="#000000"
android:textSize="10dip"
android:layout_marginTop="1dip"
android:layout_toRightOf="@+id/thumbnail"
android:text="@string/tvLastPost" />
<TextView
android:id="@+id/tv_lastUser"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tv_postUser"
android:textColor="#ffffff"
android:textSize="10dip"
android:layout_marginTop="1dip"
android:layout_marginLeft="5dp"
android:layout_toRightOf="@+id/tv_lastUser_label"
android:text="@string/tvLastPostPerson" />
<TextView
android:id="@+id/tv_lastUserDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tv_postUser"
android:textColor="#ffffff"
android:textSize="10dip"
android:layout_marginTop="1dip"
android:layout_marginLeft="5dp"
android:layout_toRightOf="@+id/tv_lastUser"
android:text="01/01/2014" />
<!-- Rightend Post Counts -->
<RelativeLayout
android:id="@+id/postcount_details"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignBottom="@id/tv_lastUser">
<LinearLayout
android:id="@+id/tv_labels"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="left">
<TextView
android:id="@+id/tv_postCount_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="right"
android:text="@string/tvTotalPosts"
android:layout_marginRight="5dip"
android:textSize="10dip"
android:textColor="#000000"
android:textStyle="bold"/>
<TextView
android:id="@+id/tv_viewCount_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="right"
android:text="@string/tvTotalViews"
android:layout_marginRight="5dip"
android:textSize="10dip"
android:textColor="#000000"
android:textStyle="bold"/>
<TextView
android:id="@+id/tv_myCount_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="right"
android:text="@string/tvMyPosts"
android:layout_marginRight="5dip"
android:textSize="10dip"
android:textColor="#000000"
android:textStyle="bold"/>
</LinearLayout>
<LinearLayout
android:id="@+id/tv_label_values"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/tv_labels"
android:orientation="vertical"
android:gravity="right">
<TextView
android:id="@+id/tv_postCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="right"
android:text="5:45"
android:layout_marginRight="5dip"
android:textSize="10dip"
android:textColor="#ffffff"
android:textStyle="bold"/>
<TextView
android:id="@+id/tv_viewCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="right"
android:text="5:45"
android:layout_marginRight="5dip"
android:textSize="10dip"
android:textColor="#ffffff"
android:textStyle="bold"/>
<TextView
android:id="@+id/tv_myCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="right"
android:text="5:45"
android:layout_marginRight="5dip"
android:textSize="10dip"
android:textColor="#ffffff"
android:textStyle="bold"/>
</LinearLayout>
</RelativeLayout>
<ImageView
android:id="@+id/tv_attachment"
android:layout_width="15dip"
android:layout_height="15dip"
android:layout_alignBottom="@id/tv_lastUser_label"
android:layout_marginRight="10dp"
android:layout_toLeftOf="@id/postcount_details"
android:src="@drawable/paperclip" />
<LinearLayout
android:id="@+id/tv_forum_details"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tv_lastUserDate"
android:orientation="horizontal"
android:layout_alignLeft="@id/tv_lastUser_label"
android:visibility="gone">
<TextView
android:id="@+id/tv_forum_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#000000"
android:textSize="10dip"
android:layout_marginTop="1dip"
android:text="@string/tvForum"/>
<TextView
android:id="@+id/tv_forum"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="1dip"
android:ellipsize="end"
android:text=""
android:textColor="#000000"
android:textSize="10dip" />
</LinearLayout>
</merge>
应用背景:这只是我经常访问的论坛的移动界面。它基本上使用JSoup来解析html,然后将该内容加载到Android用户的友好视图中。
问题描述:线程中的每个帖子都存储为一个名为“PostView”的视图。这些视图是通过ThreadFragment创建的,它负责读取线程的内容并使用数组适配器来存储和回收这些视图。
我遇到的问题是每一行自然都有自己的高度。这会导致在滚动时“闪烁”的视图,因为“PostView”的WebView的内容被创建/回收。理想情况下,我需要一种方法来第一次保存WebView的高度,这样当它需要回收视图时,它可以在加载内容时强制该高度。
答案 0 :(得分:0)
好吧,一种方法是通过ListAdapter传递TextView的高度以及数据。创建一个TextView,其中包含“已消失”的可见性以及layoutHeight:
之类的想法<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
android:id="@+id/layoutHeight" />
使用传递的高度加载TextView。
然后,覆盖ListView
的{{1}}方法并从那里设置高度。
getView()
如果有任何帮助,以下是SimpleAdapter adapter = new SimpleAdapter(MainActivity.this,receiverList, R.layout.receiver_entry, new String[] { "receiverId","receiverName", "fullPath"}, new int[] {R.id.receiverId, R.id.receiverName, R.id.fullPath}) {
@Override
public View getView (int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
TextView layoutHeightTextView = (TextView) view.findViewById(R.id.layoutHeight);
//get the value from layoutHeight and set the height of this view to that...
return view;
}
};
覆盖的示例:Create a ListView with selectable rows/change background color of ListView rows when clicked
这不是一个完美的方法。差远了。我仍然不确定为什么你的ListView会闪烁,因为getView()
应该没有闪烁就可以了。