Androidのfragmentを使った一覧ページから詳細ページへ遷移し、その後戻る場合の処理
お世話になります
Androidにて会社のアプリを開発しておりまして、
fragmentを使い一覧ページを生成して、そこから詳細に遷移するなんていう感じのものを作っています
そこで、ちょっと一般的な質問なのですが、
一覧から詳細に遷移後、また一覧に戻る際に、一覧ページでスクロールした位置に戻りたいのですが、fragmentのライフサイクルを見ると、popBackStackメソッドを使った場合
また、onCreateViewから再生成になっているので、また、viewから作りなおしなので、これはもしかして、Activityを駆使するしかないのかなと思っているのですが、実際どうなのでしょうか?
参考までに現状のソースを載せます
package jp.co.indival.shotalert.fragment;
import android.app.Activity;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.ListFragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import com.android.volley.Cache;
import com.android.volley.DefaultRetryPolicy;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonObjectRequest;
import com.android.volley.toolbox.Volley;
import com.costum.android.widget.PullAndLoadListView;
import com.costum.android.widget.PullToRefreshListView;
import com.google.android.gms.analytics.HitBuilders;
import com.google.android.gms.analytics.Tracker;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import jp.co.indival.shotalert.R;
import jp.co.indival.shotalert.adapter.WorkListAdapter;
import jp.co.indival.shotalert.common.AppController;
import jp.co.indival.shotalert.common.AppInfo;
import jp.co.indival.shotalert.common.SearchCondition;
import jp.co.indival.shotalert.common.Util;
/**
* A fragment representing a list of Items.
* <p/>
* Large screen devices (such as tablets) are supported by replacing the ListView
* with a GridView.
* <p/>
* Activities containing this fragment MUST implement the {@link OnWorkInteractionListener}
* interface.
*/
public class WorkList extends ListFragment implements View.OnClickListener,PullToRefreshListView.OnRefreshListener{
// list with the data to show in the listview
private List<JSONObject> mListItems = new ArrayList<JSONObject>();
public static final String TAG = WorkList.class.getName();
public static final String shothost = AppInfo.getShotHost();
private OnWorkInteractionListener mListener;
/**
* The fragment's ListView/GridView.
*/
private PullAndLoadListView mListView;
private TextView totalView;
private int totalCount = 0;
private int start = 0;
private int size = 20;
private int loadmax = 100;//100件以上はロードさせない
private Boolean loadmore = false;
private Boolean initload = false;
private SearchCondition cond;
private List<String> tagAry = new ArrayList<String>();
private boolean fragmentactive = false;
/**
* The Adapter which will be used to populate the ListView/GridView with
* Views.
*/
private WorkListAdapter mAdapter;
// TODO: Rename and change types of parameters
public static WorkList newInstance() {
WorkList fragment = new WorkList();
return fragment;
}
/**
* Mandatory empty constructor for the fragment manager to instantiate the
* fragment (e.g. upon screen orientation changes).
*/
public WorkList() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
fragmentactive = true;
}
@Override
public void onStart() {
super.onStart();
loadmore = false;
start = 0;
Tracker tracker = ((AppController)getActivity().getApplication()).getTracker();
tracker.setScreenName("WorkList");
tracker.send(new HitBuilders.AppViewBuilder().build());
}
/**
* 検索用のパラメータ作成
*/
private String _setParameter() {
List<String> paramAry = new ArrayList<String>();
if(cond.getCodeArea() != null){
paramAry.add("a="+cond.getCodeArea());
}
if(cond.getCodeStartday() != null){
paramAry.add("startdate="+cond.getCodeStartday());
}
if(cond.getCodePref() != null && cond.getCodePref() != ""){
paramAry.add("prefcode="+cond.getCodePref());
paramAry.add("largeareacode=L-0"+cond.getCodePref());
}
if(cond.getCodeMinimumday() != null){
paramAry.add("minimumday="+cond.getCodeMinimumday());
}
if(cond.getCodeMainjob() != null){
paramAry.add("mainjobcode="+cond.getCodeMainjob());
}
if(cond.getCodeOptionTag() != null){
paramAry.add("tags="+cond.getCodeOptionTag());
}
if(cond.getCodeHibarai() != null
&& cond.getCodeHibarai() != ""
&& !tagAry.contains(cond.getCodeHibarai())
){
tagAry.add(cond.getCodeHibarai());
}
if(cond.getCodeNoInterview() != null
&& cond.getCodeNoInterview() != ""
&& !tagAry.contains(cond.getCodeNoInterview())
){
tagAry.add(cond.getCodeNoInterview());
}
if(cond.getCodeDayoff() != null
&& cond.getCodeDayoff() != ""
&& !tagAry.contains(cond.getCodeDayoff())
){
tagAry.add(cond.getCodeDayoff());
}
if(cond.getCodeStudentok() != null
&& cond.getCodeStudentok() != ""
&& !tagAry.contains(cond.getCodeStudentok())
){
tagAry.add(cond.getCodeStudentok());
}
if(cond.getCodeShuhu() != null
&& cond.getCodeShuhu() != ""
&& !tagAry.contains(cond.getCodeShuhu())
){
tagAry.add(cond.getCodeShuhu());
}
if(cond.getCodeBeginer() != null
&& cond.getCodeBeginer() != ""
&& !tagAry.contains(cond.getCodeBeginer())
){
tagAry.add(cond.getCodeBeginer());
}
paramAry.add("start="+String.valueOf(start));
paramAry.add("results="+String.valueOf(size));
paramAry.add("output=json");
paramAry.add("apliflag=1");
paramAry.add("key_api=key_001");
if(tagAry.size() > 0){
String[] array = (String[])tagAry.toArray(new String[0]);
String tagstr = StringUtils.join(array, ",");
paramAry.add("tags[]="+tagstr);
}
String[] array = (String[])paramAry.toArray(new String[0]);
return StringUtils.join(array,"&");
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d("WorkList","===========================================");
Log.d("WorkList","onCreateView");
Log.d("WorkList","===========================================");
View view = inflater.inflate(R.layout.fragment_worklist, container, false);
cond = SearchCondition.newInstance(getActivity());
initload = true;
start = 0;
mListItems = new LinkedList<JSONObject>();
totalView = (TextView) view.findViewById(R.id.total);
Button resetbtn = (Button) view.findViewById(R.id.resetbtn);
resetbtn.setOnClickListener(this);
// Set the adapter
mListView = (PullAndLoadListView) view.findViewById(android.R.id.list);
//更新と、moreのアクションを登録
mListView.setOnRefreshListener(this);
mListView.setOnLoadMoreListener(new PullAndLoadListView.OnLoadMoreListener() {
public void onLoadMore() {
loadmore = true;
// コンテンツが追加される前に呼ばれることもある
if(mAdapter == null){
return;
}
// Do the work to load more items at the end of list
// here
//totalが開始点を超えてない場合だけ、次をロードする
if(totalCount > start+size){
start = start+size;
String param = _setParameter();
String loadurl = shothost+"sw/app/work?"+param;
_getDataByVolley(loadurl);
}else{
//((PullAndLoadListView) getListView()).onLoadMoreComplete();
}
}
});
// Set OnItemClickListener so we can be notified on item clicks
return view;
}
@Override
public void onActivityCreated (Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
start++;
//Log.d("URL","http://dev.swallow.cu01.shotlabo.info/sw/app/work?md=2&wd=2015-02-21&a=01&wtk=1&wdf=2015-02-19&key_api=key_001&output=json&start="+start+"&results="+size+"&apliflag=1");
String param = _setParameter();
Log.d("URL",shothost+"sw/app/work?"+param);
_getDataByVolley(shothost+"sw/app/work?"+param);
}
private void _loadData(String url){
String tag_json_arry = "json_array_req";
final ProgressDialog pDialog = new ProgressDialog(getActivity());
pDialog.setMessage("Loading...");
pDialog.show();
JsonObjectRequest jsonObjReq =new JsonObjectRequest(
// HTTPメソッド名を設定する。GETかPOSTか等
Request.Method.GET
// リクエスト先のURLを設定する
,url
// リクエストパラメーターを設定する
,(String)null
// 通信成功時のリスナーを設定する
,new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
// 通信成功時の処理
try {
_parseJson(response);
} catch (JSONException e) {
e.printStackTrace();
}
pDialog.dismiss();
}
}
// 通信失敗時のリスナーを設定する
,new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
// 通信失敗時の処理
pDialog.dismiss();
}
}
);
int custom_timeout_ms = 10000;
DefaultRetryPolicy pol = new DefaultRetryPolicy(custom_timeout_ms,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT);
jsonObjReq.setRetryPolicy(pol);
// Adding request to request queue
AppController.getInstance(getActivity()).addToRequestQueue(jsonObjReq, tag_json_arry);
}
/**
* _getDataFromCafhe
* volleyは通信すると自動でキャッシュに入れる(入れるとことを拒否もできるが)
* なので、キャッシュからデータを取得
* @access private
*
*
*/
private void _getDataByVolley(String url){
Log.d("URL",url);
//まずはキャッシュを確認。なければ、volleyで通信
Cache cache = AppController.getInstance(getActivity()).getRequestQueue().getCache();
Cache.Entry entry = cache.get(url);
if(entry != null){
try {
String data = new String(entry.data, "UTF-8");
JSONObject workJson = new JSONObject(data);
_parseJson(workJson);
// handle data, like converting it to xml, json, bitmap etc.,
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
Log.e(TAG,e.toString());
} catch (JSONException e) {
e.printStackTrace();
Log.e(TAG,e.toString());
}
}else{
// Cached response doesn't exists. Make network call here
_loadData(url);
}
}
/**
* _parseJson
* Jsonのパース
*
* @param workJson
*/
private void _parseJson(JSONObject workJson) throws JSONException {
JSONObject resultSet = workJson.getJSONObject("ResultSet");
if(!loadmore) {
_displayList(resultSet, workJson);
}else{
_moreList(workJson);
}
}
/**
* more用のデータセット
* @param workJson
*/
private void _moreList(JSONObject workJson) {
_setListData(workJson);
// We need notify the adapter that the data have been changed
mAdapter.notifyDataSetChanged();
// Call onLoadMoreComplete when the LoadMore task, has finished
((PullAndLoadListView) getListView()).onLoadMoreComplete();
//マックスまで達したらloadmoreリスナーは削除
if(totalCount <= start+size || start+size >= loadmax){
mListView.setOnLoadMoreListener(null);
}
}
/**
* リスト表示
* @param resultSet
* @param workJson
*/
private void _displayList(JSONObject resultSet, JSONObject workJson){
String totalStr = resultSet.optString("totalResultsAvailable");
if(totalStr != null){
totalCount = Integer.parseInt(totalStr);
}
if(totalCount == 0 && !loadmore){
setEmptyText("");
}
totalView.setText("検索結果:"+totalStr+"件");
_setListData(workJson);
//set adapter
mAdapter = new WorkListAdapter(getActivity(),R.layout.worklist_row,mListItems);
mListView.setAdapter(mAdapter);
int listsize = mAdapter.getCount();
if(listsize < 4){
mListView.mRefreshView.setMinimumHeight(0);
mListView.mRefreshView.setVisibility(View.GONE);
mListView.mRefreshViewText.setVisibility(View.GONE);
ViewGroup.LayoutParams lp = mListView.mRefreshViewProgress.getLayoutParams();
ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams)lp;
mlp.setMargins(0,0,0,0);
mListView.mRefreshViewProgress.setLayoutParams(mlp);
mListView.mRefreshViewText.setLayoutParams(mlp);
mListView.mRefreshView.setPadding(0, 0, 0, 0);
}
if(!initload){
((PullAndLoadListView) getListView()).onRefreshComplete();
}else{
initload = false;
}
//20件以下なら次は読み込まない
if(totalCount <= size){
mListView.setOnLoadMoreListener(null);
}
}
/**
*
* @param workJson
*/
private void _setListData(JSONObject workJson){
JSONArray jsonAry = workJson.optJSONArray("Result");
for (int i = 0; i < jsonAry.length(); i++) {
JSONObject json = jsonAry.optJSONObject(i);
mListItems.add(json);
}
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (OnWorkInteractionListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnFragmentInteractionListener");
}
}
@Override
public void onDetach() {
super.onDetach();
mListener = null;
}
@Override
public void onListItemClick(ListView l, View view, int position, long id) {
String workid = mListItems.get(position-1).optString("WorkId");
// if (null != mListener) {
// // Notify the active callbacks interface (the activity, if the
// // fragment is attached to one) that an item has been selected.
// String workid = mListItems.get(position-1).optString("WorkId");
// mListener.onWorkInteraction(workid);
// }
// フラグメントのインスタンスを生成する。
Fragment newFragment = Web.newInstance();
FragmentTransaction ft = getFragmentManager().beginTransaction();
// ↓アクション指定
ft.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left);
// Layout位置先の指定
ft.replace(R.id.container, newFragment);
Bundle bundle = new Bundle();
bundle.putString("url",shothost+"sw/detail/"+workid+"?mc=04");
Log.d("DETAIL",shothost+"sw/detail/"+workid+"?mc=04");
// フラグメントに渡す値をセット
newFragment.setArguments(bundle);
ft.addToBackStack("worklist");
ft.commit();
}
/**
* The default content for this Fragment has a TextView that is shown when
* the list is empty. If you would like to change the text, call this method
* to supply the text it should use.
*/
public void setEmptyText(CharSequence emptyText) {
View emptyView = mListView.getEmptyView();
if (emptyView instanceof TextView) {
((TextView) emptyView).setText("条件に該当するワークは見つかりませんでした");
}
}
@Override
public void onClick(View v) {
if (null != mListener) {
// Notify the active callbacks interface (the activity, if the
// fragment is attached to one) that an item has been selected.
mListener.onResetCondition();
}
}
@Override
public void onRefresh() {
loadmore = false;
start = 0;
mListItems = new ArrayList<JSONObject>();
start++;
String param = _setParameter();
String loadurl = shothost+"sw/app/work?"+param;
_getDataByVolley(loadurl);
}
/**
* This interface must be implemented by activities that contain this
* fragment to allow an interaction in this fragment to be communicated
* to the activity and potentially other fragments contained in that
* activity.
* <p/>
* See the Android Training lesson <a href=
* "http://developer.android.com/training/basics/fragments/communicating.html"
* >Communicating with Other Fragments</a> for more information.
*/
public interface OnWorkInteractionListener {
// TODO: Update argument type and name
public void onWorkInteraction(String workid);
public void onResetCondition();
}
}
ご教授お願いします