编辑:我没有为这个对话框发布我的 XML。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tag_layout"
android:orientation="vertical"
android:layout_height="wrap_content"
android:layout_width="@dimen/min_dialog_width"
android:padding="5dp"
android:animateLayoutChanges="true"
>
<!-- Here is the view to show if the list is emtpy -->
<TextView
android:id="@android:id/empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="50dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_centerInParent="true"
android:gravity="center"
android:text="@string/no_items"
android:visibility="invisible"
/>
<ListView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="invisible"
/>
<ProgressBar
android:id="@+id/tag_spin_progress_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:indeterminate="true"
/>
</RelativeLayout>
我正在使用 android.support.v4.CursorLoader 和 CursorAdapter,我正试图让它更新它的光标。在 Android 2.3.3 中它工作得很好。但是,当我在我的 4.0.3 设备上尝试它时,ListView 不会刷新,并且永远不会调用我的适配器中的 newView 方法。我知道光标中有数据,因为我可以在我的 2.3.3 设备上看到它。
如果我旋转我的设备,ListView 会显示我想要的内容。我已尝试使 ListView 无效,但这并没有解决问题。
如果我不重置 ListView 的适配器,列表不会变为空白,但它仍然不会刷新列表。
我在嵌入 DialogFragment 的扩展 AlertDialog 中执行所有这些操作。
这是整个类(class)
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.database.Cursor;
import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.*;
import org.lds.ldssa.service.MLDatabase;
import org.lds.ldssa.service.aws.Annotation;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class TagDialog extends AlertDialog implements LoaderManager.LoaderCallbacks<Cursor>,AdapterView.OnItemClickListener {
private static final String TAG = "ldssa.tagdialog";
public static final int TAGLOADERID = 0;
// View Items
private EditText mEditText;
private ListView mListView;
private TextView mEmptyView;
private ProgressBar mProgressBar;
private ImageButton mNewTagButton;
private ImageButton mSortTagButton;
private TextView mTitle;
private String mTagTitle;
private String mNewTagTitle;
private Annotation mAnnotation;
private ContentFragment mContentFragment;
private boolean isNewTagView;
private static final String KEY_NEWTAGVIEW = "new_tag_view";
private static final String POSITION_KEY = "TAG_POSITION";
private static final String Y_KEY = "TAG_Y";
private static final String SORT_KEY = "TAG_SORT";
private static final String CHECKED_STATE_KEY = "TAG_CHECKED_STATE";
private static final int NOT_SET = -1;
private int mPosition;
private int mY;
private TagSuggestionAdapter mSuggestionAdapter;
private TagListAdapter mTagAdapter;
private MLDatabase mlDatabase;
private boolean mSortAlpha;
private HashMap<Long, CheckedState> mCheckedState;
protected TagDialog(Context context) {
super(context);
}
public void onCreate(Bundle savedInstanceState){
Context context = getContext();
Resources r = context.getResources();
final LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.dialog_tag, null);
// Main parts of the view
mEditText = (EditText) view.findViewById(R.id.tag_new_tag);
mListView = (ListView) view.findViewById(android.R.id.list);
mProgressBar = (ProgressBar) view.findViewById(R.id.tag_spin_progress_bar);
mEmptyView = (TextView) view.findViewById(android.R.id.empty);
mEmptyView.setVisibility(View.INVISIBLE);
// Titlebar
View titleBar = inflater.inflate(R.layout.dialog_tag_title, null);
mNewTagButton = (ImageButton) titleBar.findViewById(R.id.tag_new_icon);
mSortTagButton = (ImageButton) titleBar.findViewById(R.id.tag_sort_icon);
mTitle = (TextView) titleBar.findViewById(R.id.tag_title);
mTagTitle = r.getString(R.string.tag_dialog_title);
mNewTagTitle = r.getString(R.string.tag_new_dialog_title);
this.setCustomTitle(titleBar);
// Buttons
final String OK = r.getString(R.string.ok);
final String CANCEL = r.getString(R.string.cancel);
this.setButton(BUTTON_POSITIVE, OK, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) { /*Never Used*/}});
this.setButton(BUTTON_NEGATIVE, CANCEL, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) { /*Never Used*/}});
// Setup Button Listeners
setOnShowListener(new OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
Button ok = getButton(BUTTON_POSITIVE);
ok.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(isNewTagView){
hideIMM();
addNewTag();
mEditText.setText("");
setupTagDialog();
} else {
Collection<CheckedState> changes = mCheckedState.values();
boolean success = true;
MLDatabase db = getDatabase();
db.beginAnnotationTransaction();
for(CheckedState change : changes){
if(!change.checked()){
//Detag
db.detagAnnotation(mAnnotation.getDbKey(), change.tagID());
} else {
mAnnotation.saveHighlightsToDatabase(db);
if(mAnnotation.getDbKey().intValue() != MLDatabase.NOT_SET_INT &
change.tagID() != MLDatabase.NOT_SET_INT){
success = db.tagAnnotation(mAnnotation.getDbKey(), change.tagID(), change.changed());
}
}
}
if(success){
db.setAnnotationTransactionSuccessful();
}
db.endAnnotationTransaction();
mCheckedState.clear();
dismiss();
}
}
});
Button cancel = getButton(BUTTON_NEGATIVE);
cancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(isNewTagView){
hideIMM();
setupTagDialog();
mEditText.setText("");
} else {
mCheckedState.clear();
dismiss();
}
}
});
}
});
mNewTagButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
setupNewTagDialog();
}
});
mSortTagButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mSortAlpha = !mSortAlpha;
restartLoader();
}
});
mListView.setOnItemClickListener(TagDialog.this);
mEditText.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) {
LoaderManager lm = getLoaderManager();
if(lm != null){
Loader l = lm.getLoader(TAGLOADERID);
if(l != null){
l.forceLoad();
} else {
restartLoader();
}
} else {
restartLoader();
}
}
});
//Handle Rotations
if(savedInstanceState == null){
//New
mPosition = NOT_SET;
mY = NOT_SET;
mSortAlpha = false;
mCheckedState = getCheckedState(mAnnotation.getDbKey());
isNewTagView = false;
} else {
//rotated
isNewTagView = savedInstanceState.getBoolean(KEY_NEWTAGVIEW, false);
mPosition = savedInstanceState.getInt(POSITION_KEY, NOT_SET);
mY = savedInstanceState.getInt(Y_KEY, NOT_SET);
mSortAlpha = savedInstanceState.getBoolean(SORT_KEY, false);
restoreCheckedState(savedInstanceState);
}
mTagAdapter = new TagListAdapter(context, null, mCheckedState);
mSuggestionAdapter = new TagSuggestionAdapter(context, null, 0);
LoaderManager lm = getLoaderManager();
if(lm != null){
lm.initLoader(TAGLOADERID, null, this);
}
this.setView(view);
super.onCreate(savedInstanceState);
}
private void addNewTag() {
String tag = mEditText.getText().toString().trim();
if(!tag.equals("")){
getDatabase();
Integer langID = mAnnotation.getLanguageId();
try{
long tagID = mlDatabase.insertTag(langID, tag);
if(mAnnotation.getDbKey().intValue() != MLDatabase.NOT_SET_INT &&
tagID != MLDatabase.NOT_SET_INT){
mCheckedState.put(tagID, new CheckedState(tagID, true, true));
}
} catch (Exception e) {
Log.d(TAG, "Problem saving new tag: " + tag + " : " + e.getMessage());
e.printStackTrace();
}
}
}
public void onStart(){
if(isNewTagView){
setupNewTagDialog();
} else {
setupTagDialog();
}
restartLoader();
}
@Override
public Bundle onSaveInstanceState(){
Bundle bundle = super.onSaveInstanceState();
//Save What dialog we are in.
bundle.putBoolean(KEY_NEWTAGVIEW, isNewTagView);
bundle.putBoolean(SORT_KEY, mSortAlpha);
//Save position
bundle.putInt(POSITION_KEY, mListView.getFirstVisiblePosition());
final View v = mListView.getChildAt(0);
bundle.putInt(Y_KEY, (v == null) ? 0 : v.getTop());
//Save Checked State
Iterator it = mCheckedState.entrySet().iterator();
int i = 0;
while(it.hasNext()){
Map.Entry pair = (Map.Entry)it.next();
bundle.putSerializable(CHECKED_STATE_KEY + i, (CheckedState)pair.getValue());
i++;
}
bundle.putInt(CHECKED_STATE_KEY, i);
return bundle;
}
private void restoreCheckedState(Bundle bundle){
int count = bundle.getInt(CHECKED_STATE_KEY);
mCheckedState = new HashMap<Long, CheckedState>();
boolean success = true;
for(int i = 0; i < count; i++){
CheckedState cs = (CheckedState)bundle.getSerializable(CHECKED_STATE_KEY+i);
if(cs == null){
success = false;
break;
}
mCheckedState.put(cs.tagID(), cs);
}
if(!success){
mCheckedState = getCheckedState(mAnnotation.getDbKey());
}
}
@Override
public void onBackPressed(){
if(isNewTagView){
hideIMM();
setupTagDialog();
} else {
this.dismiss();
}
}
private void setupTagDialog() {
isNewTagView = false;
mTitle.setText(mTagTitle);
mNewTagButton.setVisibility(View.VISIBLE);
mSortTagButton.setVisibility(View.VISIBLE);
mEmptyView.setVisibility(View.INVISIBLE);
mEditText.setVisibility(View.GONE);
mListView.setVisibility(View.GONE);
mProgressBar.setVisibility(View.VISIBLE);
mListView.setAdapter(mTagAdapter);
restartLoader();
}
private void setupNewTagDialog() {
isNewTagView = true;
mTitle.setText(mNewTagTitle);
mNewTagButton.setVisibility(View.INVISIBLE);
mSortTagButton.setVisibility(View.INVISIBLE);
mEmptyView.setVisibility(View.INVISIBLE);
mEditText.setVisibility(View.VISIBLE);
mListView.setVisibility(View.GONE);
mProgressBar.setVisibility(View.VISIBLE);
mListView.setAdapter(mSuggestionAdapter);
restartLoader();
}
public void setAnnotation(Annotation a) {
mAnnotation = a;
}
public void setContentViewInterface(ContentFragment contentFragment) {
mContentFragment = contentFragment;
}
private MLDatabase getDatabase() {
if(mlDatabase == null){
GospelLibraryApplication app = (GospelLibraryApplication) getContext().getApplicationContext();
mlDatabase = app.getMlDatabase();
}
return mlDatabase;
}
public String getFilter() {
return mEditText.getText().toString().trim();
}
public Integer getAnnotationID(){
if(mAnnotation != null){
return mAnnotation.getDbKey();
}
return MLDatabase.NOT_SET_INT;
}
private LoaderManager getLoaderManager(){
if(mContentFragment == null){
Log.d(TAG, "ContentFragment is NULL!");
return null;
}
return mContentFragment.getContentActivity().getSupportLoaderManager();
}
private void restartLoader(){
LoaderManager lm = getLoaderManager();
if(lm != null){
lm.restartLoader(TAGLOADERID, null, this);
}
}
private void hideIMM(){
InputMethodManager imm = (InputMethodManager)getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mEditText.getWindowToken(), 0);
}
private HashMap<Long, CheckedState> getCheckedState(Integer annotationID) {
HashMap<Long, CheckedState> checkedState = new HashMap<Long, CheckedState>();
MLDatabase db = getDatabase();
Cursor cursor = db.queryAllTagsWithAnnotation(annotationID);
if(cursor != null){
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()){
Long tagID = cursor.getLong(cursor.getColumnIndex(MLDatabase.CL_ID));
boolean isChecked = !cursor.isNull(cursor.getColumnIndex(MLDatabase.CL_ANNOTATION));
checkedState.put(tagID, new CheckedState(tagID, isChecked, false));
}
}
return checkedState;
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
TagCursorLoader loader = new TagCursorLoader(getContext(), this);
return loader;
}
@Override
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor data) {
if(isNewTagView) {
mSuggestionAdapter.changeCursor(data);
if(mListView.getAdapter() == null){
mListView.setAdapter(mSuggestionAdapter);
}
} else {
mTagAdapter.changeCursor(data);
if(mListView.getAdapter() == null){
mListView.setAdapter(mTagAdapter);
}
}
if(mPosition != NOT_SET && mY != NOT_SET){
mListView.setSelectionFromTop(mPosition, mY);
mPosition = mY = NOT_SET;
}
if (mListView.getAdapter() != null) {
if (mListView.getAdapter().getCount() > 0) {
mEmptyView.setVisibility(View.INVISIBLE);
}
else {
mEmptyView.setVisibility(View.VISIBLE);
}
}
else {
mEmptyView.setVisibility(View.VISIBLE);
}
mProgressBar.setVisibility(View.GONE);
mListView.setVisibility(View.VISIBLE);
mListView.invalidate();
}
@Override
public void onLoaderReset(Loader<Cursor> cursorLoader) {
if(mSuggestionAdapter != null) {
mSuggestionAdapter.changeCursor(null);
}
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if(isNewTagView){
TextView tv = (TextView)view;
mEditText.setText(tv.getText());
Button ok = getButton(BUTTON_POSITIVE);
if(ok != null){
ok.performClick();
}
} else {
CheckedTextView ctv = (CheckedTextView)view;
boolean checked = !ctv.isChecked();
ctv.setChecked(checked);
mCheckedState.put(id, new CheckedState(id, checked, true));
}
}
public static class TagCursorLoader extends CursorLoader {
private final ForceLoadContentObserver mObserver = new ForceLoadContentObserver();
private TagDialog dialog;
private MLDatabase mlDatabase;
private Cursor mCursor;
private String mFilter;
private Integer mAnnotationID;
// Runs on worker thread
@Override
public Cursor loadInBackground(){
Cursor cursor = null;
if(dialog.isNewTagView){
mFilter = dialog.getFilter();
cursor = mlDatabase.getTagSuggestions(mFilter);
} else {
cursor = mlDatabase.queryTags(dialog.mSortAlpha);
}
if(cursor != null){
cursor.registerContentObserver(mObserver);
}
return cursor;
}
//Runs on UI thread
@Override
public void deliverResult(Cursor cursor){
//Handle if canceled in the middle.
if(isReset()){
if(cursor != null){
cursor.close();
}
return;
}
Cursor oldCursor = mCursor;
mCursor = cursor;
if(isStarted()) {
super.deliverResult(cursor);
}
if(oldCursor != null && !oldCursor.equals(cursor) && !oldCursor.isClosed()) {
oldCursor.close();
}
}
public TagCursorLoader(Context context, TagDialog dialog) {
super(context);
this.dialog = dialog;
mlDatabase = dialog.getDatabase();
}
@Override
public void onStartLoading(){
if(mCursor == null) {
forceLoad();
} else {
if(dialog.isNewTagView && mFilter.equals(dialog.getFilter())) {
deliverResult(mCursor);
} else {
forceLoad();
}
}
}
@Override
protected void onStopLoading() {
// Attempt to cancel the current load task if possible.
cancelLoad();
}
@Override
public void onCanceled(Cursor cursor) {
if (cursor != null && !cursor.isClosed()) {
cursor.close();
}
}
@Override
protected void onReset() {
super.onReset();
// Ensure the loader is stopped
onStopLoading();
if (mCursor != null && !mCursor.isClosed()) {
mCursor.close();
}
mCursor = null;
}
}
/**
* Class is used to store the temporary checked state of the tags.
*/
public class CheckedState implements Serializable {
private static final long serialVersionUID = 1263560458217339487L;
/**
* @serialField
*/
private long tagID;
/**
* @serialField
*/
private boolean checked;
/**
* @serialField
*/
private boolean changed;
/**
* Constructor for CheckedState.
* @param tagID The tag ID
* @param checked The Current Checked State
* @param changed Ture if changed in the dialog. False if pulling from database.
*/
public CheckedState(long tagID, boolean checked, boolean changed){
this.tagID = tagID;
this.checked = checked;
this.changed = changed;
}
public long tagID(){
return tagID;
}
public boolean checked() {
return checked;
}
public boolean changed() {
return changed;
}
}
}
最佳答案
注意我之前没有添加我的 XML。 这就是问题所在。
andriod:animateLayoutChanges
不适用于我尝试做的事情。
一旦我将它从我的 XML 中删除,它就非常有效。
关于Android 4.0.3 CursorAdapter 不会在 changeCursor 上填充 ListView,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10741773/
我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%
设置:狂欢ruby1.9.2高线(1.6.13)描述:我已经相当习惯在其他一些项目中使用highline,但已经有几个月没有使用它了。现在,在Ruby1.9.2上全新安装时,它似乎不允许在同一行回答提示。所以以前我会看到类似的东西:require"highline/import"ask"Whatisyourfavoritecolor?"并得到:Whatisyourfavoritecolor?|现在我看到类似的东西:Whatisyourfavoritecolor?|竖线(|)符号是我的终端光标。知道为什么会发生这种变化吗? 最佳答案
我在我的Rails项目中使用Pow和powifygem。现在我尝试升级我的ruby版本(从1.9.3到2.0.0,我使用RVM)当我切换ruby版本、安装所有gem依赖项时,我通过运行railss并访问localhost:3000确保该应用程序正常运行以前,我通过使用pow访问http://my_app.dev来浏览我的应用程序。升级后,由于错误Bundler::RubyVersionMismatch:YourRubyversionis1.9.3,butyourGemfilespecified2.0.0,此url不起作用我尝试过的:重新创建pow应用程序重启pow服务器更新战俘
我有一个驼峰式字符串,例如:JustAString。我想按照以下规则形成长度为4的字符串:抓取所有大写字母;如果超过4个大写字母,只保留前4个;如果少于4个大写字母,则将最后大写字母后的字母大写并添加字母,直到长度变为4。以下是可能发生的3种情况:ThisIsMyString将产生TIMS(大写字母);ThisIsOneVeryLongString将产生TIOV(前4个大写字母);MyString将生成MSTR(大写字母+tr大写)。我设法用这个片段解决了前两种情况:str.scan(/[A-Z]/).first(4).join但是,我不太确定如何最好地修改上面的代码片段以处理最后一种
最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路
我遇到了一个非常奇怪的问题,我很难解决。在我看来,我有一个与data-remote="true"和data-method="delete"的链接。当我单击该链接时,我可以看到对我的Rails服务器的DELETE请求。返回的JS代码会更改此链接的属性,其中包括href和data-method。再次单击此链接后,我的服务器收到了对新href的请求,但使用的是旧的data-method,即使我已将其从DELETE到POST(它仍然发送一个DELETE请求)。但是,如果我刷新页面,HTML与"new"HTML相同(随返回的JS发生变化),但它实际上发送了正确的请求类型。这就是这个问题令我困惑的
我有可变数量的表格和可变数量的行,我想让它们一个接一个地显示,但如果表格不适合当前页面,请将其放在下一页,然后继续。我已将表格放入事务中,以便我可以回滚然后打印它(如果高度适合当前页面),但我如何获得表格高度?我现在有这段代码pdf.transactiondopdf.table@data,:font_size=>12,:border_style=>:grid,:horizontal_padding=>10,:vertical_padding=>3,:border_width=>2,:position=>:left,:row_colors=>["FFFFFF","DDDDDD"]pdf.
我正在尝试解决http://projecteuler.net/problem=1.我想创建一个方法,它接受一个整数,然后创建一个包含它前面的所有整数的数组,并将整数本身作为数组中的值。以下是我目前所拥有的。代码不起作用。defmake_array(num)numbers=Array.newnumcount=1numbers.eachdo|number|numbers 最佳答案 (1..num).to_a是您在Ruby中需要做的全部。1..num将创建一个Range对象,以1开始并以任意值num结束是。Range对象有to_a方法通过
如果我想要“00001”而不是“1”,除了我自己写填零方法之外,有没有内置的方法可以帮助我为整数填零? 最佳答案 puts"%05d"%1#00001参见:String::%,Kernel::sprintf这是正在发生的事情。%左侧的"%05d"是C风格的格式说明符。%右边的变量就是要格式化的东西。格式说明符可以像这样解码:%-格式说明符的开头0-用前导零填充5-长度为5个字符d-被格式化的是一个整数如果你要格式化多个东西,你会把它们放在一个数组中:"%d-%s"%[1,"One"]#=>1-one
使用RubyonRails,我使用给定的增量(例如每30分钟)用时间填充“选择”。目前我正在YAML文件中写出所有的可能性,但我觉得有一种更巧妙的方法。我想我想提供一个开始时间、一个结束时间、一个增量,并且目前只提供一个名为“关闭”的选项(想想“business_hours”)。所以,我的选择可能会显示:'Closed'5:00am5:30am6:00am...[allthewayto]...11:30pm谁能想出更好的方法,或者只是将它们全部“拼写”出来的最佳方法? 最佳答案 此答案基于@emh的答案。defcreate_hour