안드로이드앱이 어느날 갑자기 게시 삭제가 되어 메일을 확인해보니



Issue: Violation of Device and Network Abuse policy 

We don’t allow apps that interfere with, disrupt, damage, or access in an unauthorized manner the user’s device, other devices or computers, servers, networks, application programming interfaces (APIs), or services, including but not limited to other apps on the device, any Google service, or an authorized carrier’s network.

Your app shouldn’t access or use the service or API in a manner that violates its terms of service. For example, your app shouldn't download, monetize, or access YouTube videos in a way that violates the YouTube Terms of Service.

Submit your app for another review

Read through the Device and Network Abuse policy and make appropriate changes to your app.
Make sure that your app is compliant with all other Developer Program Policies. Additional enforcement could occur if there are further policy violations.
Sign in to your Play Console and submit the update to your app.
If you’ve reviewed the policy and feel our decision may have been in error, please reach out to our policy support team. One of my colleagues will get back to you within 2 business days.

Thanks for your continued support of Google Play.

Best,

The Google Play Review Team


이런 내용의 메일이 와있었다.

이 내용으로만은 왜 삭제하였는지 알 수 없어 문의를 다시 하였더니 screen off(파워버튼)를 하여도 웹뷰에 있는 재생되고있는 유투브 비디오가 멈추지 않는다고

설명을 해주었다.


확인해보니 모든 앱에서 그런건 아니고 롤리팝 아래버전에서 스크린 off가 되어도 플레이되고 있던 비디오가 멈추지않고 돌아

화면은 꺼져있는데 비디오의 사운드가 계속 재생되는 문제가 보였다.




try{
    ((AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE)).requestAudioFocus(
            new AudioManager.OnAudioFocusChangeListener() {
                @Override
                public void onAudioFocusChange(int focusChange) {}
            }, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
}catch (Exception e){ }


위의 소스를 onPause() 때 넣어주니

screen off때 비디오 사운드가 들리던 문제가 해결되었다.

///////////////////////////////////////

추가) 2019.01.17

이 코드를 적용할때 문제가 발견됨.

시스템 오디오를 off시켜버림.

예를들어 멜론플레이어로 음악을 들을때, 내 앱을 사용하다가 해당 소스가 실행되면 음악이 중지됨.

적용시 주의필요.


///////////////////////////////////////

추가) 2019.02.21


오디오를 정지시킬 필요없이 아래소스를 적용하면된다. 뻘짓했네;;

@Override
public void onPause(){
    super.onPause();

    try {
       webView.onPause();
    } catch(Exception e) {}
}

pause에 정지시켰으니 마찬가지로 resume에서는 webView.onResume(); 을 꼭 시켜주자


앱에서만 로드되어야 하는 js, css가 있어서

WebViewClient의 onPageFinished 시점에 js 를 embed 시키기 위해 코드를 작성하였다.



public void injectCss(WebView wv, String link){
    if(!"".equalsIgnoreCase(link)){
        try{
            wv.loadUrl(
                    "javascript:(function() { " +
                            "var css = document.createElement('link'); " +
                            "css.rel = 'stylesheet'; " +
                            "css.type = 'text/css'; " +
                            "css.href = '" + link + "'; " +
                            "document.getElementsByTagName('head')[0].appendChild(css)" +
                            "})()"
            );
        }catch (Exception e){}
    }
}

public void injectJs(WebView wv, String link){
    if(!"".equalsIgnoreCase(link)){
        try{
            wv.loadUrl(
                    "javascript:(function() { " +
                            "var js = document.createElement('script'); " +
                            "js.type = 'text/javascript'; " +
                            "js.src = '" + link + "'; " +
                            "document.getElementsByTagName('body')[0].appendChild(js)" +
                            "})()"
            );
        }catch (Exception e){}
    }
}


onPageFinished 시점 위 함수를 호출해주면 해당 link의 js나 css를 embed 시켜주는 걸 확인 할 수 있다.

( 크롬 인스펙터로 확인하면된다 )

하이브리드 앱인데 사이트의 html을 수정을 하였지만

캐시가 남아 웹뷰에서 깨지는 현상이 발견되어

새로고침 버튼을 누를시에 확실히 캐시를 날리고 싶어 확인하던중

이미 웹뷰에 clear cache를 하고있는 것을 확인했다.



public void onPageFinished(WebView view, String url) {
   webView.clearHistory();
   webView.clearCache(true);
   webView.clearView();
   super.onPageFinished(view, url);
}


그러나 그래도 뭔가 부족했다...(캐시가 확실히 안지워지는 느낌)
웹뷰에 캐시가 남은게 아니라 앱 캐시가 남아서 그런게 아닐까 라는 생각에

그냥 캐시 디렉토리를 날려버리기 위해 소스를 추가했다.



public void clearApplicationCache(Context context, File file) {
    File dir = null;
    if (file == null) {
        dir = context.getCacheDir();
    } else {
        dir = file;
    }

    if (dir == null) return;

    File[] children = dir.listFiles();
    try {
        for (int i = 0; i < children.length; i++)
            if (children[i].isDirectory())
                clearApplicationCache(context, children[i]);
            else
                children[i].delete();
    } catch (Exception e) {
    }
}


이렇게 함수를 만든후


새로고침 버튼을 눌렀을때
clearApplicationCache(mContext, null);
이렇게 사용하니
확실히 설정에서 확인 할 수있는 앱 메모리 크기가 눈에띄게 줄어드는걸 확인 할 수 있었다.


캐싱이 짜증나서 페이지 로딩때 마다 클리어해버리면
트래픽이 과도하게 증가할수있으니 특정 버튼(새로고침 버튼)을 눌렀을때 처럼 특정 상황일때만

캐시 디렉토리를 날리게 하는것이 바람직할것 같은 생각이 든다.


기존 프로젝트에서 RecyclerView를 통해 내용을 쭉 불러오고 있었는데

실험삼아 검색기준을 바꾸어 1000개정도의 아이템을 한번에 불러왔더니 

역시나 로딩이 오래걸리면서 앱이 버벅거렸다.


그래서 해결책을 찾던 중 페이징(맨 하단에 1,2,3.... 이런식으로 페이지번호를 이용한 기능)이 생각났지만

그것보다는 n개 씩 보여주는 대신 맨 끝으로 스크롤 되면 그다음(n+1 ~ 2n), 또 끝으로 스크롤되면 그다음(2n+1 ~ 3n) 을

불러오는 [새로고침 아이템 추가] 방식이 더 좋을것 같아서 찾아보았다.


찾아보던중 SwipeRefreshLayout 이라는 위젯을 발견하였는데 이와 비슷한 기능을 도와주었다.

하지만 내가 원하는 기능은 아니였다.


내가 원하는 기능은 밑에서 위로 스크롤하여 맨밑에 도달 하였을 때의 이벤트를 잡아야 하는데

해당 SwipeRefreshLayout는 위에서 밑으로 스크롤하여 맨 위에 도달 하였을 때 프로그레스가 생성되며

리플래시 리스너를 실행시켜주는 위젯이었다.

(쉽게 말하면 내가 원하는 기능은 페이스북처럼 밑에서 위로 스크롤 하면서 과거의 item들을 추가로 가져오는 기능)



내가 원하는 기능을 구현한 사이트를 겨우 찾았는데

https://www.youtube.com/watch?v=PqJAKA5IEF8


위의 동영상을 보면 알겠지만 맨위와 맨밑 새로고침을 전부 구현한 것이라

내 입맛대로 필요없는 부분(맨위 새로고침을 제외) 을 걸러내고 다시 만들어보았다.


///////////////////////////////////////////////////////////////////////////////



MainActivity.class

public class MainActivity extends AppCompatActivity implements AdapterItem.OnLoadMoreListener {

private AdapterItem mAdapter;
private ArrayList<Item> itemList;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

itemList = new ArrayList<Item>();
RecyclerView mRecyclerView = (RecyclerView) findViewById(R.id.rvList);
LinearLayoutManager mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
mAdapter = new AdapterItem(this);
mAdapter.setLinearLayoutManager(mLayoutManager);
mAdapter.setRecyclerView(mRecyclerView);
mRecyclerView.setAdapter(mAdapter);


}

@Override
protected void onStart() {
super.onStart();
Log.d("MainActivity_", "onStart");
loadData();
}


//스크롤이 끝에 도달하였을 때 실행 내용
@Override
public void onLoadMore() {
Log.d("MainActivity_", "onLoadMore");
mAdapter.setProgressMore(true);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
itemList.clear();
mAdapter.setProgressMore(false);

///////이부분에을 자신의 프로젝트에 맞게 설정하면 됨
//다음 페이지? 내용을 불러오는 부분
int start = mAdapter.getItemCount();
int end = start + 15;
for (int i = start + 1; i <= end; i++) {
itemList.add(new Item("Item " + i));
}
//////////////////////////////////////////////////
mAdapter.addItemMore(itemList);
mAdapter.setMoreLoading(false);
}
}, 2000);
}

private void loadData() {
itemList.clear();
for (int i = 1; i <= 20; i++) {
itemList.add(new Item("Item " + i));
}
mAdapter.addAll(itemList);
}

}


AdapterItem.class

public class AdapterItem extends RecyclerView.Adapter<RecyclerView.ViewHolder>  {
private final int VIEW_ITEM = 1;
private final int VIEW_PROG = 0;
private ArrayList<Item> itemList;

private OnLoadMoreListener onLoadMoreListener;
private LinearLayoutManager mLinearLayoutManager;

private boolean isMoreLoading = false;
private int visibleThreshold = 1;
int firstVisibleItem, visibleItemCount, totalItemCount, lastVisibleItem;

public interface OnLoadMoreListener{
void onLoadMore();
}

public AdapterItem(OnLoadMoreListener onLoadMoreListener) {
this.onLoadMoreListener=onLoadMoreListener;
itemList =new ArrayList<>();
}

public void setLinearLayoutManager(LinearLayoutManager linearLayoutManager){
this.mLinearLayoutManager=linearLayoutManager;
}

public void setRecyclerView(RecyclerView mView){
mView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
visibleItemCount = recyclerView.getChildCount();
totalItemCount = mLinearLayoutManager.getItemCount();
firstVisibleItem = mLinearLayoutManager.findFirstVisibleItemPosition();
lastVisibleItem = mLinearLayoutManager.findLastVisibleItemPosition();
Log.d("total", totalItemCount + "");
Log.d("visible", visibleItemCount + "");

Log.d("first", firstVisibleItem + "");
Log.d("last", lastVisibleItem + "");

if (!isMoreLoading && (totalItemCount - visibleItemCount)<= (firstVisibleItem + visibleThreshold)) {
if (onLoadMoreListener != null) {
onLoadMoreListener.onLoadMore();
}
isMoreLoading = true;
}
}
});
}

@Override
public int getItemViewType(int position) {
return itemList.get(position) != null ? VIEW_ITEM : VIEW_PROG;
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEW_ITEM) {
return new StudentViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_text, parent, false));
} else {
return new ProgressViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_progress, parent, false));
}

}

public void addAll(List<Item> lst){
itemList.clear();
itemList.addAll(lst);
notifyDataSetChanged();
}

public void addItemMore(List<Item> lst){
itemList.addAll(lst);
notifyItemRangeChanged(0,itemList.size());
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof StudentViewHolder) {
Item singleItem = (Item) itemList.get(position);
((StudentViewHolder) holder).tvItem.setText(singleItem.getItem());
}
}

public void setMoreLoading(boolean isMoreLoading) {
this.isMoreLoading=isMoreLoading;
}

@Override
public int getItemCount() {
return itemList.size();
}

public void setProgressMore(final boolean isProgress) {
if (isProgress) {
new Handler().post(new Runnable() {
@Override
public void run() {
itemList.add(null);
notifyItemInserted(itemList.size() - 1);
}
});
} else {
itemList.remove(itemList.size() - 1);
notifyItemRemoved(itemList.size());
}
}

static class StudentViewHolder extends RecyclerView.ViewHolder {
public TextView tvItem;

public StudentViewHolder(View v) {
super(v);
tvItem = (TextView) v.findViewById(R.id.tvItem);
}
}

static class ProgressViewHolder extends RecyclerView.ViewHolder {
public ProgressBar pBar;
public ProgressViewHolder(View v) {
super(v);
pBar = (ProgressBar) v.findViewById(R.id.pBar);
}
}
}


Item.class

public class Item  {

private String item;

public String getItem() {
return item;
}

public void setItem(String item) {
this.item = item;
}


public Item(String item) {
this.item=item;
}

}



activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/cl_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleMarginEnd="64dp"
app:expandedTitleMarginStart="48dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed">


<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="400dp"
android:fitsSystemWindows="true"
android:theme="@style/AppTheme.AppBarOverlay">

<android.support.design.widget.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleMarginEnd="64dp"
app:expandedTitleMarginStart="48dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed">


<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:popupTheme="@style/AppTheme.PopupOverlay"
app:title="TEST" />

</android.support.design.widget.CollapsingToolbarLayout>

</android.support.design.widget.AppBarLayout>

<android.support.v7.widget.RecyclerView
android:id="@+id/rvList"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
/>

</android.support.design.widget.CoordinatorLayout>



item_progress.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<ProgressBar
android:id="@+id/pBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminate="true"/>

</LinearLayout>



item_text.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content">

<TextView
android:id="@+id/tvItem"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="30sp"/>

</LinearLayout>



styles.xml

<resources>

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>

<style name="NoTitle" parent="Theme.AppCompat.Light.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>


<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />

</resources>





참고한 소스 : https://github.com/esantiago1/RecyclerViewEndlessScroll

액티비티간 데이터를 넘길 때 intent.putExtra, getExtra를 이용해서 넘기는데

난 문자열이나 정수, 실수형 데이터만 넘겨지는 줄 알았다.


문득 객체도 전달 가능한지 궁금하여 검색하니 

역시나 객체도 전달 할 수 있었다.


Parcelable 인터페이스를 구현하는 객체를 만들어 전달하면 된다고 해서 따라해봤다.


////////////////////////////////////////////////////////////


먼저 Parcelable 인터페이스를 상속받아 넘길 객체의 클래스를 만든다


import android.os.Parcel;

import android.os.Parcelable;


public class SaleItem implements Parcelable{


    private String RNUM;

    private int PLNM_NO;


    public SaleItem() {


    }


    private SaleItem(Parcel in) {

        this.RNUM = in.readString();

        this.PLNM_NO = in.readString();

    }


    public static final Parcelable.Creator<SaleItem> CREATOR = new Parcelable.Creator<SaleItem>() {

        public SaleItem createFromParcel(Parcel in) {

            return new SaleItem(in);

        }

        public SaleItem[] newArray (int size) {

            return new SaleItem[size];

        }

    };


    @Override

    public int describeContents() {

        return 0;

    }


    @Override

    public void writeToParcel(Parcel dest, int flags) {

  //생성자에서 읽어오는 순서와 기록하는 순서가 같아야 한다고 한다!!!!!

        dest.writeString(this.RNUM);

        dest.writeInt(this.PLNM_NO);

    }

}


//////////////////////////////////////////////////////////


객체 넘길 때


Intent it = new Intent(context, SaleDetailActivity.class);
it.putExtra("sale_item", item);

context.startActivity(it);


//////////////////////////////////////////////////////////


객체 받을 때


SaleItem saleItem = (SaleItem) getIntent().getParcelableExtra("sale_item");


recyclerview 를 사용하던중 스크롤을 하는데 뭔가 스무스 하지 않는 현상이 일어났다.


스크롤을 내리는데 뭔가 답답하고 천천히 내려가는 느낌? 이다


///////////////////////////////////////////////////////


rvSaleList.setNestedScrollingEnabled(false);


setNestedScrollingEnabled(false); 를 리사이클뷰에 설정해주면 된다

에디트 텍스트를 클릭하면 키보드가 올라오는데

입력을 마치고 뒤로가기로 키보드를 내리지 않고 다른 버튼을 눌렀을 떄

키보드를 내리게 하고 싶다면


/////////////////////////////////////////////


private InputMethodManager imm;

imm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);


이런식으로 InputMethodManager 를 선언한 후


키보드를 내리고 싶을 떄


imm.hideSoftInputFromWindow(this.getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);


요렇게 사용해주면 키보드가 내려간다..

fab이 구성되어있는 화면인데 editText를 누르면 보통 키보드가 올라온다.


그런데 문제는 키보드가 올라오면서 밑에있던 fab이 키보드 위로 올라와버린다

(키보드가 올라오면서 화면을 밀어올리는 듯한 현상)



요렇게... 밑에있던 fab이 키보드위로 올라와버림


//////////////////////////////////////////////////////////////////////////


해결방법을 찾던중 


AndroidManifest.xml 파일에서

해당 Activity에 android:windowSoftInputMode="adjustNothing" 을 추가해주면 된다고 하여 해보니 성공하였다!


<activity
android:name=".main.MainActivity"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustNothing">
</activity>

요렇게 추가해주면 된다.


결과



공공데이터 API를 http request로 호출하였는데 반환형식이 xml 이었다.

내가 이니 만든 request 공통 모듈은 반환형식을 string 으로 받고 있었기 때문에

xml 이 그냥 쭉 스트링으로 저장되어 반환되었다.


string 으로 된 xml 을 파싱하려고하니 불편할것 같아서

string 을 xml에 다시 담은 뒤 xml을 파싱하려고 한다.


////////////////////////////////////////////////


//string -> xml


  //string을 xml로 바꾸어 넣을 곳

  XmlPullParser parser = Xml.newPullParser();


try {

 // http request 후 xml을 스트링값으로 받는 부분

            data = new JOAsyncTask().execute().get();


//스트링을  다시 xml로 

            parser.setInput(new StringReader(data));


        } catch (InterruptedException e) {

            e.printStackTrace();

        } catch (ExecutionException e) {

            e.printStackTrace();

        } catch (XmlPullParserException e){

            e.printStackTrace();

        }


return parser;



///////////////////////////////////////////////////

//xml 의 형태


<?xml version="1.0" encoding="UTF-8"?>


<response>



<header>


<resultCode>00</resultCode>


<resultMsg>NORMAL SERVICE.</resultMsg>


</header>



<body>



<items>


<item>


<RNUM>1</RNUM>


<CTGR_ID>10000</CTGR_ID>


<CTGR_NM>부동산</CTGR_NM>


<CTGR_HIRK_ID>1</CTGR_HIRK_ID>


<CTGR_HIRK_NM>ONBID</CTGR_HIRK_NM>


</item>



<item>


<RNUM>2</RNUM>


<CTGR_ID>11000</CTGR_ID>


<CTGR_NM>권리/증권</CTGR_NM>


<CTGR_HIRK_ID>1</CTGR_HIRK_ID>


<CTGR_HIRK_NM>ONBID</CTGR_HIRK_NM>


</item>



<item>


<RNUM>3</RNUM>


<CTGR_ID>12000</CTGR_ID>


<CTGR_NM>자동차/운송장비</CTGR_NM>


<CTGR_HIRK_ID>1</CTGR_HIRK_ID>


<CTGR_HIRK_NM>ONBID</CTGR_HIRK_NM>


</item>



</items>



<pageNo>1</pageNo>



<totalCount>22</totalCount>



<numOfRows>999</numOfRows>


</body>


</response>





//////////////////////////////////////////////////


//xml 파싱


public ArrayList<UseCodeTop> xmlData(XmlPullParser parser){


        ArrayList<UseCodeTop> list = new ArrayList<>();


        try {

            int eventType = parser.getEventType();


            UseCodeTop data = null;


            while (eventType != XmlPullParser.END_DOCUMENT) {

                switch (eventType) {


                    case XmlPullParser.START_TAG:


                        String startTag = parser.getName();


                        if ("item".equals(startTag)) {

                            data = new UseCodeTop();

                        } else if ("RNUM".equals(startTag)) {

                            data.setRNUM(parser.nextText());

                        } else if ("CTGR_ID".equals(startTag)) {

                            data.setCTGR_ID(parser.nextText());

                        } else if ("CTGR_NM".equals(startTag)) {

                            data.setCTGR_NM(parser.nextText());

                        }

                        break;


                    case XmlPullParser.END_TAG:

                        String endTag = parser.getName();

                        if ("item".equals(endTag)) {

                            list.add(data);

                        }

                        break;

                }

                eventType = parser.next();

            }


        } catch (XmlPullParserException e) {

            e.printStackTrace();

        } catch (IOException e) {

            e.printStackTrace();

        }


        Log.d("UseCodeTop_CNT", list.size() +"");


        //데이터 확인용

        /*Iterator<UseCodeTop> iterator = list.iterator();

        while (iterator.hasNext()) {

            UseCodeTop tmp = (UseCodeTop) iterator.next();

            Log.d("hohoho", tmp.getCTGR_NM().toString());

        }*/


        return list;


    }


공공데이터 (www.data.go.kr) 의 API를 쓸일이 있어서 회원가입도하고 순조롭게하고있는데

갑자기 막혀버렸다


인증키를 발급받는데 까지 후다닥왔는데

인증키를 받고 해당 사이트에서 미리 테스트 해볼수 있는데 

(상세기능정보 -> 미리보기 다운로드 실행 -> 샘플데이터 입력 후 미리보기 클릭)


결과 xml 에

resultCode 가 30으로 나오고

resultMsg 가 SERVICE_KEY_IS_NOT_REGISTERED_ERROR


이런식으로 나왔다 ㅡㅡ


//////////////////////////////////////////////////////////////////


http://www.data.go.kr/pubn/lab/gui/IrosDevGuide/openDataDetailPagePubc.do?pblonsipResrcePk=



찾아보니 등록되지 않은 서비스키라고 하길래

자세히 보니 인증키 발급 후 1시간 이후에 사용 가능하단다


그런데 난 3시간 정도 기다려도 안되더라...ㅡㅡ

기다리다 못해 인증키 재발급을 눌려놓고 재발급 받은 후

자고 일어나서 해보니 정상적으로 작동되었다.




this.finish() 로 액티비티를 종료하는 동시에


나는 메소드가 그 시점에서 멈추고 끝내는 줄 알았다.ㅎㄷㄷ



예를 들어 다음과 같은 코드가 있는데


//onCreate

 protected void createActivity(Bundle savedInstanceState) {

        View view = this.setContainerView(R.layout.activity_main);

        getSupportActionBar().setTitle("경성대 헬퍼");


        if (getIntent().getExtras() != null && getIntent().getExtras().getString("state").equals("kill")) {

            final Intent intent = new Intent(this, SignupActivity.class);

            startActivity(intent);

            this.finish();

            //return;

        }

        checkChatId();

}



값을 받아서 값이 kill이면 액티비티를 종료후 다른 액티비티로 넘어가는 기능인데

this.finish()를 하면 밑에있는 checkChatId(); 를 실행하지 않고 액티비티가 종료 될것이라고 생각했는데

checkChatId(); 가 실행되었다.... 

완전 잘못생각하고있었던것 같다.


this.finish()를 한후 return을 해주니 밑에 함수들이 실행되지 않았다.


카카오톡 로그인을 사용하고 있는데 액티비티 구조가 이렇다...


1 . Login -> Main 하면서 Login액티비티 finish

2 . Main -> Setup 하면서 Main액티비티 finish하지 않음

3.  Setup액티비티에  앱 연결 해지 기능 있음

4.  앱 연결 해지 하면 Login 액티비티로 이동



여기서 문제가 발생했다.

앱 연결 해지 후 Login액티비티로 이동하는데 Main액티비티가 살아있어

뒤로가기하면 앱이 종료되는것이 아니라 Main액티비티로 이동해버린다...


///////////////////////////////////////////////////////////////////////////////////////


해결방법에는 여러가지가 있었는데

새로운 액티비티를 열때마다 액티비티를 저장해놓고 한꺼번에 종료시키는 방법

finishAffinity() 사용 등이 있었는데

나 같은 경우는 액티비티를 하나만 더 종료시켜주면 되니까 putExtra를 사용하기로 했다.


앱 연결 해지 버튼을 누르면


protected void redirectSignupActivity() {

        final Intent intent = new Intent(this, MainActivity.class);

        intent.putExtra("state", "kill");

        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

        startActivity(intent);

        this.finish();

    }


이 메소드를 실행시키게 한다

이때 LoginActivity로 바로 인텐트 하지않고 Main액티비티를 거쳐 가게끔 한다

setFlags를 FLAG_ACTIVITY_CLEAR_TOP으로 줘서

기존에 쌓여있는 main액티비티만을 남기고 나머지 activity를 종료시킨다.

(FLAG_ACTIVITY_CLEAR_TOP 플래그를 주게되면 해당 Main액티비티가 OnCreate부터 다시 실행된다)


그리고 MainActivity의 OnCreate에


if (getIntent().getExtras() != null && getIntent().getExtras().getString("state").equals("kill")) {

            final Intent intent = new Intent(this, LoginActivity.class);

            startActivity(intent);

            this.finish();

        }


이렇게 해놓으면 "kill"값을 받고

LoginActivity를 띄운다음

MainActivity는 죽게된다.


그리고 LoginActivity에서 뒤로가기를 누르게되면 Main으로 넘어가지않고

앱이 종료되는것을 알 수 있다.

갑자기 안드로이드 스튜디오를 실행하여

VCS Update Project를 선택하여  학교에서 커밋한 프로젝트를

집에서 업데이트 받으려고 했는데

 이런 에러가 발생하였다.


Error:svn: E155004: Run 'svn cleanup' to remove locks (type 'svn help cleanup' for details)

svn: E155004: Working copy '[프로젝트경로...]' locked.

svn: E155004: '[프로젝트경로...]' is already locked.


svn에 뭔가 에러가 발생한거 같긴한데 구글을 검색하니

svn에 직접 접속하여 cleanup 명령어를 실행하라는 등 여러가지 해결방안이 있었는데

어떻게 해야하는지 이해도 되지 않고 헤깔렸다.


그런데 어떻게하다보니 너무 쉽게 해결되었다....


///////////////////////////////////////////////////////////////////////////////



VCS 메뉴에서 Cleanup Project를 클릭하니 바로 해결되었다...


개발하던 앱이 마쉬멜로우(M) 버전을 타겟으로 만들고 있었는데

M버전 이상 버전으로는 권한체크 방식이 달라졌다고 들어서


갤럭시 노트4(os 버전이 마쉬멜로우)를 빌려 그곳에 컴파일 해보았는데

해당 Dangerous permissions 을 사용하는 기능에서 

에러가 나면서 앱이 종료되버렸다.

(사실 앱이 종료까지 될 줄은 몰랐다. 해당 기능만 실행 안되고 말줄 았았는데

앱이 강제 종료되버렸다.)


내가 사용한 Dangerous permissions 에는 


ACCESS_FINE_LOCATION

CALL_PHONE

WRITE_EXTERNAL_STORAGE

GET_ACCOUNTS


요렇게 있었는데 해결 방법을 찾던 중

좋은 블로그를 발견해서 여기를 참고하여 해결했다.

http://gun0912.tistory.com/55



이 사이트에서는 라이브러리도 제공하는데

TedPermission 라이브러리를 이용하여

쉽게 권한문제를 해결했다.


https://github.com/ParkSangGwon/TedPermission


해당 Dangerous permissions 을 사용하는 곳에서 

기능을 사용하기전에 권한 체크를한 후

권한이 있을때 작업을 진행하는 구조인것 같다.


////////////////////////////////////////////////////////////////////////////


권한 체크 작업을 하던중 기존에 작동하던 기능이 안되는 부분을 발견하여 

확인해보았는데 



<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />


이 부분의 권한이 문제를 이르켰다.


푸쉬알람이 오면 앱안이든 앱밖이던 언제든지 윈도우 다이얼로그 팝업을 띄우려고(최상위 뷰) 사용한 권한인데

해당 기능에 퍼미션 문제를 이르키며 작동되지 않았다


TedPermission 라이브러리를 이용해서 처리해보려 했는데 해결되지 않았다.


확인해보니 해당 권한은 [앱] -> [설정] -> [권한] 에서 확인 하여 허용하는 권한이 아니라

[다른 앱 위에 그릴 수 있는 앱]이라는 설정 화면 에서 허용을 해줘야하는 권한이었다.


https://brunch.co.kr/@babosamo/49


그래서 해당 부분 권한은 따로 처리하였는데


public final static int REQUEST_CODE = 3333;


public void startOverlayWindowService(Context context) {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(context)) {

            Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));

            startActivityForResult(intent, REQUEST_CODE);

        }

    }



@Override

protected void onActivityResult(int requestCode, int resultCode,  Intent data) {

    if (requestCode == REQUEST_CODE) {

        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {

            if (Settings.canDrawOverlays(this)) {

                Toast.makeText(MainActivity.this, "오버레이 권한 확인 완료", Toast.LENGTH_SHORT).show();

            } else {

                Toast.makeText(MainActivity.this, "오버레이 권한이 없습니다.", Toast.LENGTH_SHORT).show();

            }

        }

    }

}


이런식으로 처리를 해주었다.

listview를 만드는데 adpater의 getview에서 onClickListener를 매겼다

그런데 처음 띄웠을때의 리스트 아이템은 제대로 값이 들어갔지만


스크롤하여 밑쪽 데이터를 누르니 다른 데이터가 나왔다...


///////////////////////////////////


if (convertView == null) {


}


이 안에 온클릭리스너를 잡아주었는데

이 안에서 데이터를 넣으면 안된다고한다

(convertView를 재사용하기 때문에 라고 한다)


if문 밖에서 리스너를 잡아주니 제대로 작동 되었다.


참고 사이트

http://myelf.egloos.com/2723978

액티비티가 아닌 곳에서 전화 걸기 화면으로 넘기기 위한 방법


////////////////////////////////


Context를 선언하여 생성자에서 context를 미리 받아놓자

(객체생성할때 this를 넘기자)


private Context mContext;


public 생성자(Context context){

mContext = context;

}


private void startCall(String num) {

        Intent it = new Intent(Intent.ACTION_CALL, Uri.parse("tel:" + num));

        it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

        mContext.startActivity(it);

}


//////////////////////////////////


근데 저렇게 써왔던것 같은데 startActivity에서 에러가 났다 ㅡㅡ

에러 알림에서 해결방법을 클릭했더니


private void startCall(String num) {

        Intent it = new Intent(Intent.ACTION_CALL, Uri.parse("tel:" + num));

        it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

        if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {

            // TODO: Consider calling

            //    ActivityCompat#requestPermissions

            // here to request the missing permissions, and then overriding

            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,

            //                                          int[] grantResults)

            // to handle the case where the user grants the permission. See the documentation

            // for ActivityCompat#requestPermissions for more details.

            return;

        }

        mContext.startActivity(it);

    }


이런식으로 권한체크 if문이 추가되었다!!! 

마시멜로우 부터 권한 부분이 달라졌다고 들었는데 그 때문인것 같다. 

(마시멜로우 전에는 앱 설치시 모든 권한을 다 허가받고 설치를 했는데 

후 부터는 각 기능을 실행하기전에 권한을 확인한다고 한다??)


아무튼 권한여부를 이제 각각 체크해줘야 하는것 같은데 좀 더 알아보고 해야겠다.

참고할만한 사이트는 찾았다.


http://gun0912.tistory.com/55

fastScroll 작업때문에 첫번째 리스트 아이템의 첫번쨰 글자의 자음이 필요했다.


검색하여 간단하게 자음을 가져오는 방법을 알게됬다.


///////////////////////////



private static final char HANGUL_BEGIN_UNICODE = 44032; // 가

private static final char HANGUL_LAST_UNICODE = 55203; // 힣

private static final char HANGUL_BASE_UNIT = 588;//각자음 마다 가지는 글자수

private static final char[] INITIAL_SOUND = { 'ㄱ', 'ㄲ', 'ㄴ','ㄷ', 'ㄸ', 'ㄹ', 'ㅁ', 'ㅂ', 'ㅃ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅉ','ㅊ', 

'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ' };


private char getInitialSound(char c) {

            int hanBegin = (c - HANGUL_BEGIN_UNICODE);

            int index = hanBegin / HANGUL_BASE_UNIT;

            return INITIAL_SOUND[index];

        }


String을 st.charAt(0) 으로 때서 함수에 넣어주면

자음을 반환해 준다.


ex)

String test = "테스트 입니다";


char result = getInitialSound(test.charAt(0));


or


String result = getInitialSound(test.charAt(0)) + "" ;


이렇게 하면 'ㅌ'가 나오게 된다.

액티비티에 editText를 사용하면

액티비티가 실행될 때 자동으로 editText가 포커싱 되면서

키보드가 자동으로 올라오게 된다.  

너무 보기안좋았다....


/////////////////////////////////////


해결방법


<activity

            android:name=".call.CallActivity"

            android:windowSoftInputMode="stateAlwaysHidden"

            android:screenOrientation="portrait" 

/>


요렇게 AndroidManifest.xml 에서  android:windowSoftInputMode="stateAlwaysHidden" 를 

액티비티 속성에 넣어주면 해당 엑티비티에서는 실행할때 에디트텍스트가 있어도 

키보드를 띄우지 않게된다. (그래도 포커싱은 된다)


다른방법으로는 강제로 다른 뷰에 포커싱을 줘버리면 된다.

볼리 라이브러리의 NetworkImageView 를 사용하는 중인데


불러올 이미지가 없을때 디폴트 이미지를 지정하기위해


<com.android.volley.toolbox.NetworkImageView

                android:id="@+id/niv_user_image"

                android:layout_width="match_parent"

                android:layout_height="match_parent"

                android:src="@drawable/ic_anonymous"

                android:scaleType="centerCrop" />


이런식으로 사용하였는데 src 부분이 먹질 않았다.


/////////////////////////////////////


검색해보니 java 소스상에서 밑에 내용을 추가해주면 디폴트이미지와 

이미지를 불러오기 실패했을 때의 기본이미지를 지정해 줄 수 있다.


userImage.setDefaultImageResId(R.drawable.kakao_default_profile_image);

userImage.setErrorImageResId(R.drawable.kakao_default_profile_image);

개발하다보니 NestedScrollView 안에 RecyclerView 를 넣게되었는데

스크롤 동작은 정상적으로 작동되었지만

RecyclerView부분이 화면밖으로 나갔을 떄 그쪽까지 스크롤이 되지 않거나

아에 뷰가 안생기는 현상을 확인하였다


대충 아래와같은 그림이었는데...


///////////////////////////////////////////


<LinearLayout>


<android.support.v4.widget.NestedScrollView>


<LinearLayout>


<android.support.v7.widget.CardView>

</android.support.v7.widget.CardView>


<android.support.v7.widget.RecyclerView>

</android.support.v7.widget.RecyclerView>


</LinearLayout>

</android.support.v4.widget.NestedScrollView>

</LinearLayout>


/////////////////////////////////////////////


생각해보니 RecyclerView의 height길이가 고정이 아니라 NestedScrollView의 전체길이에

포함되지 않아 RecyclerView 부분이 잘린것 같다고 생각이 들었다.


그래서 RecyclerView의 height 속성에 고정으로 박아주니 그 크기만큼 잘 표시되었다.

(ex) 

<android.support.v7.widget.RecyclerView

    android:id="@+id/rv_lost_comment_list"

          android:layout_width="match_parent"

          android:layout_height="300dp"

/>


하지만 나같은 경우는 리사이클뷰에 아이템이 없을수도있고, 많이있을수도 있는데

저렇게 300dp로 고정하게되니 없어도 무조껀 저만큼 생기게되어 옮바른 해결방법이 아니라 생각하였다.


/////////////////////////////////////////////////////////


그래서 찾게된 해결방법은

xml에서가 아니라 java소스상에서 리사이클뷰에 들어갈 아이템 개수가 정해지면

그때 리사이클뷰의 height에 고정 길이를 부여하면 될것같아서 해보았다.


private void resizeCommentList(int item_size){

        ViewGroup.LayoutParams params = rvLostCommentList.getLayoutParams();

        params.height = 200 * item_size;

        rvLostCommentList.setLayoutParams(params);

}

//여기서 rvLostCommentList 는 findviewbyid로 잡은 리사이클뷰


이런식으로 아이템 갯수가 정해지면

(아이템 갯수) X (아이템 하나에 해당하는 예상 고정 길이)

해서 나온 값을 리사이클뷰의 height 속성에 부여하게 되면

아이템 갯수에 따라 길이가 잘 조정되었다.


리스트뷰 대신에 RecyclerView 를 이용하여 게시판을 만들어보려고 하고있는데


화면에서 분명히 RecyclerView의 width를 match_parrent로 하여 옆으로 꽉차게 하였는데


적용되지가 않았다 (물론 item도 width는 match_parrent)


////////////////////////////////////////////////////////


검색해보니 어댑터 부분의 onCreateViewHolder 부분을 수정해주면 된다고 하여 수정해보았다.


원래 아래와같이 


   @Override

    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_lost, null);

        return new ViewHolder(v);

    }


이런식으로 작성하였었는데 아래와 같이 작성하니



   @Override

    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_lost, parent, false);

        return new ViewHolder(v);

    }


해결되었다.


원인은 


Parameters
resource ID for an XML layout resource to load (e.g., R.layout.main_page) root
view to be the parent of the generated hierarchy (if attachToRoot is true), or else simply an object that provides a set of LayoutParams values for root of the returned hierarchy (if attachToRoot is false.)
attachToRoot Whether the inflated hierarchy should be attached to the root parameter? If false, root is only used to create the correct subclass of LayoutParams for the root view in the XML. Returns The root View of the inflated hierarchy. If root was supplied and attachToRoot is true, this is root; otherwise it is the root of the inflated XML file.


It is important here to not supply true, but do supply the parent:


Supplying the parent View lets the inflater know what layoutparams to use. Supplying the falseparameter tells it to not attach it to the parent just yet. That is what the RecyclerView will do for you.


요렇다는데 아직 이해가 안간다. 시간날때 다시 해석해봐야겠다

갑자기 android studio 에서 에러가 발생했다


FILE 메뉴의 project structure 메뉴를 클릭했는데 에러가 발생하면서 

실행도 안되고


오후 3:33:32 IllegalArgumentException: Multiple entries with same key: Google Inc.:

Google APIs (x86 System Image):19=Google APIs (x86 System Image) (API 19) and 

Google Inc.:Google APIs (x86 System Image):19=Google APIs (x86 System Image) (API 19)


이런 에러를 띄웠다


//////////////////////////////////////////////////////


sdk 19 를 지웠다가 다시 설치하니 정상 작동되었다 ㅡㅡ;;


다시 설치안하고 지우기만해도 정상 작동된다...

약 60개의 버튼이 필요해서 

버튼을 만들고 각각 findviewbyId를 하고 onclickListener를 주려고 생각하고 

노가다로 막 만들던 중 배열을 써서 하면 편하겠다고 생각하여 다시 해보았다.



/////////////////////////////////////////////////////////////


private Button[][] btnTime = null;
///////////////////////////////////

btnTime = new Button[5][11];
int[][] btnId = { {R.id.btn_mon_1, R.id.btn_mon_2, R.id.btn_mon_3, R.id.btn_mon...... },

    {생략},

생략

}


for(int i = 0; i < 5; i++){
for(int j = 0; j < 11; j++){
this.btnTime[i][j] = (Button) findViewById(btnId[i][j]);
}
}

for(int i = 0; i < 5; i++){
for(int j = 0; j < 11; j++){
this.btnTime[i][j].setOnClickListener(btnListener);
}
}
///////////////////////////////////////////////
private View.OnClickListener btnListener = new View.OnClickListener() {

@Override
public void onClick(View v) {

for(int i = 0; i < 5; i++){
for(int j = 0; j < 11; j++){
if(v.getId() == btnTime[i][j].getId()) {
btnTime[i][j].setText(i + " " + j);
}
}
}
}
};

////////////////////////////////////////////


R.id 들을 int배열에 담고 마찬가지로 button도 배열로 담아놓은뒤에

반복문을 이용해 findviewbyid를 해주고

클릭 리스너도 마찬가지로 반복문을 이용해 set해줍니다

다음지도를 사용하려고 sdk를 다운받아 app에 넣고

구현하고 있는데 


맵의 중심점을 변경하려고 다음지도에서 해당 위치의 좌표를 구하려고 했는데

찾을 수 없었다. (옛날에는 지원해줬다는데 좌표보여주는 기능을 없앴다고 한다...)


그래서 찾은 방법은 구글지도에서 해당 위치를 검색해서

거기서 나온 좌표값을 이용하면 되길래 그렇게 사용하고 있다.


////////////////////////////////////////////////////////////////////


구글지도에서 좌표찾는 방법 = http://isntyet.tistory.com/28



위에서 찾은 좌표를 아래와 같이 적용시키면 된다


// 중심점 변경
mapView.setMapCenterPoint(MapPoint.mapPointWithGeoCoord(37.53737528, 127.00557633), true);


////////////////////////////////////////////////////////////////////////

추가


좌표를 바로 보여주는 사이트

http://mygeoposition.com/

Activity에 시계가 필요해서

찾던중 디지털시계를 사용하려면 DigitalClock이라는 위젯이 있어서 사용하던중


시간변화에 따라 어떤 특정 작업을 하고싶어서 

찾아보니 addTextChangedListener를 사용하면 된다고 한다


/////////////////////////////////////


public class TestActivity extends BaseActivity {

private TextView stop;
private DigitalClock currentTime;

@Override
protected void createActivity() {
View view = this.setContainerView(R.layout.activity_bus);

upStop = (TextView) view.findViewById(R.id.up_stop);

currentTime = (DigitalClock) view.findViewById(R.id.current_time);
currentTime.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) {
upStop.setText(s);
}
});

}

}


<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginTop="10dp"
android:gravity="center"
>

<TextView         android:id="@+id/upStop"

android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:textSize="40sp"
android:text="test"
android:textStyle="bold"
/>

<DigitalClock
android:id="@+id/current_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="-20dp"
android:textSize="90sp"
/>
</LinearLayout>


이렇게 하면 afterTetChanged에 있는 upStop.setText(s); 가 

시간이 변할 때 마다 작동하게 됩니다.


/////////////////////////////////////////////////////////

추가

@Override
public void afterTextChanged(Editable s) {
upStop.setText(s);
}


이부분에서 Editable s는 현재 시스템 시간을 가져오는건데

폰에서 설정을 어떻게 하느냐에 따라  시간 포맷이 다릅니다


예를들어 시스템시간을 '24시간 형식 사용' 설정으로 해놓으면

16:00시 18:12 이런식으로 나오지만

안해놓으면 오후 4:00 오후 6:12  이런식으로 나오니

주의해야 합니다


setImageResource() 를 이용해서 들어가있는 이미지를 비우고 싶다면

null을 넣지말고


view.setImageResource(0);


이런식으로 0값을 넣어주면 된다

깃허브에서 HoloColorPicker(https://github.com/LarsWerkman/HoloColorPicker) 를 

적용시켜서 사용하던중 선택한 칼라 값을 가져와서 작업을 하려고 값을 확인하는데


값이 -124123 -124124 -124545 등   마이너스 값들이 막 나왔다


HEX값으로 변환 (FF0500 이런 값)을 하고 싶어서 찾아보니



String hexColor = String.format("#%06X", (0xFFFFFF & intColor));

이것을 적용하면 되겠다 싶었다.


///////////////////////////////////////////////


@Override
public void onColorChanged(int color) {
String hexColor = String.format("#%06X", (0xFFFFFF & color));

Log.d("testPrint", hexColor);
}


volley 기능을 추상클래스로 만들고 -> Request


그 추상클래스를 상속받아 파라미터와 추가URL을 설정하는 클래스를 만들고 ->TestRequest


그 클래스의 객체를 만들어 실행하는 ->TestActivity


구조를 만들고 싶어서 만들고있는데...

되긴되지만 아직 미완성이다... 더 공부해야겠다


/////////////////////////////////////////////////////////////////

파라미터를 겟방식으로 url에 붙이는 것을 추가하는 것과

response 결과 데이터를 가공하는 것에대해 더 수정해야할것같다

비동기에대한 공부를 더해봐야겠다


//////////////////////////////////////////////////////////////////


Request.java


import android.app.ProgressDialog;
import android.content.Context;
import android.util.Log;

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.JsonRequest;

import org.json.JSONObject;

import java.util.HashMap;


public abstract class Request {
private HashMap<String, String> params = new HashMap<String, String>();
private ProgressDialog loadingDialog;

private Context context = null;
private String baseUrl = "http://11.22.33.44/hahaha/";
private String urlName;
private String resultString="";

public Request(Context context) {
this.context = context;
}

public void request(final VolleyCallback callback) {
RequestQueue queue = VolleyUtill.getInstance(context).getRequestQueue();
loadingDialog = ProgressDialog.show(context, "불러오는 중입니다..","Please wait..", true, false);

JsonObjectRequest jsObjRequest = new JsonObjectRequest(
JsonRequest.Method.GET, baseUrl+urlName, (String)null,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
Log.i("Response", response.toString());

resultString = response.toString();
callback.onSuccess(resultString);
loadingDialog.dismiss();
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.i("Response_error", error.toString());
loadingDialog.dismiss();
}
}
);

// Get the ImageLoader through your singleton class.
//mImageLoader = VolleyUtill.getInstance(this).getImageLoader();

VolleyUtill.getInstance(context).addToRequestQueue(jsObjRequest);
}

public interface VolleyCallback{
void onSuccess(String result);
}

/*
public Response.Listener<JSONObject> createMyReqSuccessListener() {
return new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
Log.i("Response", response.toString());
//String stringFromJson = parseJSON(response);
result = response.toString();
//setResultString(response.toString());
loadingDialog.dismiss();
}
};
}

public Response.ErrorListener createMyReqErrorListener() {
return new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.i("Response error","errorr!!!!!!!");
//result = "fail";
//setResultString(error.toString());
loadingDialog.dismiss();
}
};
}*/

public void clearParams() {
this.params = new HashMap<String, String>();
}

public void setParam(String key, String value) {
this.params.put(key, value);
}

public void setUrlName(String urlName) {
this.urlName = urlName;
}

}


TestRequest.java


import android.content.Context;
import android.util.Log;

//import com.~~~~~~~~~~~~~.Request;

import org.json.JSONArray;
import org.json.JSONObject;

import java.util.HashMap;


public class TestRequest extends Request {
private String result;

public TestRequest (Context context) {
super(context);
}

public void setParams(String userId) {
super.clearParams();
super.setParam("user_id", userId);
}

public void startRequest(final VolleyCallback callback ) {
setUrlName("select_test.php");

request(callback);
}

public HashMap<String, String> jsondata(String data){
HashMap<String, String> resData = new HashMap<String, String>();

try{
JSONObject jsonobj = new JSONObject(data);

JSONArray jsonary = jsonobj.getJSONArray("result");

for(int i = 0; i < jsonary.length(); i++){
JSONObject obj = jsonary.getJSONObject(i);
resData.put("user_id", obj.getString("user_id"));
resData.put("test2", obj.getString("test2"));
resData.put("test3", obj.getString("test3"));
}

}catch (Exception e){
Log.d("json error ===", e.getMessage());
}

return resData;
}
}


TestActivity.java


import android.util.Log;
import android.view.View;
import android.widget.TextView;

import java.util.HashMap;

/**
* Created by jo on 2016-03-19.
*/
public class BusActivity extends BaseActivity {

TextView mTextView;
private HashMap<String, String> busList = null;

@Override
protected void createActivity() {
View view = this.setContainerView(R.layout.activity_bus);
getSupportActionBar().setTitle("테스트");

mTextView = (TextView) view.findViewById(R.id.text);

onBusRequest();
}

private void onBusRequest(){
BusRequest request = new BusRequest(this);
request.setParams("23");

request.startRequest(new Request.VolleyCallback() {
@Override
public void onSuccess(String result) {
Log.i("hahahah", result);
}
});
}

@Override
protected void destroyActivity() {

}

@Override
protected void viewClick(View view) {
if (view.getId() == R.id.btn_back) {
this.finish();
}
}

}


안드로이드 개발을 하고있는데

로그를 자주 찍다보니 로그캣창을 자주 보게 되는데

글자색이 크게 구분이 안가서 눈이 아팠다ㅠ


글자색을 바꾸기로 결심했다


////////////////////////////////////////


File -> Settings -> Editor -> Colors & Fonts -> Android Logcat   에 들어간다


그리고 Use inherited attributes를 체그해제하고 색상을 변경한다

Save As로 설정내용을 따로 저장하여 설정을 수정하는것이 좋은듯 하다


수정완료하고 Ok를 누르면 된다

'공부 > Android' 카테고리의 다른 글

HoloColorPicker 값 가져오기 HEX값  (0) 2016.05.11
(android volley) 공통화 하기  (0) 2016.05.08
(eclipse) svn plugin 설치  (0) 2016.04.15
(eclipse) Eclipse ADT Plugin 설치  (0) 2016.04.15
(Android Studio) naver svn 멤버추가  (0) 2016.04.04

이클립스 Help 메뉴의


Eclipse Marketplace에 들어갑니다



svn을 검색하여 맨위에 Subversive의 install 을 클릭하여 밑에 과정에 따라 설치한다






이렇게 설치가 끝나면 이클립스를 리스타트 하게 됩니다

그리고 open perpective 를 선택하여

SVN Repository Exploring을 선택하게 되면.....






밑에 창이 뜨는 것을 확인 할 수 있는데

svn 커넥터를 설치해줘야 svn을 사용할 수 있기때문에 설치해줍니다

SVN kit 1.8.11을 설치해 줍니다




설치완료하면 svn 설정 완료입니다






+ Recent posts