기본 콘텐츠로 건너뛰기

안드로이드 앱 만들기 : 리사이클뷰(RecycleView) 에 광고 추가


원본출처: 티스토리 바로가기

앱을 만들다 보면 앱에 광고에 넣고 싶다. 그런데 화면에는 리사이클뷰가 들어있는데 그안에는 어떻게 넣을 것인가 ? 그것을 찾다 보니...

 

https://github.com/googleads/googleads-mobile-android-examples

 

GitHub - googleads/googleads-mobile-android-examples: googleads-mobile-android

googleads-mobile-android. Contribute to googleads/googleads-mobile-android-examples development by creating an account on GitHub.

github.com

 

여기 까지 찾아갔다.  그 안을 찾다보니, BannerRecyclerViewExample 이라는 프로젝트 파일을 찾을 수 있었다. 이 예제의 내용은 menu(식당에서 말하는) item 을 넣은 recycleview 에 banner 광고를 추가하는 식으로 예제를 보여 주고 있다. 

 

물론 나도 recycleview 에 데이터를 넣은 다음 그안에 banner 광고를 추가할 생각이니 대략 다름이 없게 되는 것이다.

admob 광고를 설정하는 것은 이전 포스팅을 참고해서 하고...

 

https://billcorea.tistory.com/57

 

안드로이드 앱 만들기 도전 4일차 admob 달아 보기

오늘은 내가 만든 앱에 광고를 달아보자... admob 으로 다가... 그래서 먼저 할 꺼는 admob 에 로그인하고 앱 만들기를 클릭하기 그럼 다음 그림과 같이 나옴.  나의 앱은 안드로이드 버전이고, 아직

billcorea.tistory.com

 

일단 layout 에 recycleview 을 넣어 보자.

<?xml version="1.0" encoding="utf-8"?> <LinearLayout 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:layout_width="match_parent"     android:layout_height="match_parent"     android:orientation="vertical"     android:weightSum="30"     tools:context=".MainActivity">      <LinearLayout         android:id="@+id/base_progressBar"         android:layout_width="match_parent"         android:layout_height="match_parent"         android:background="@drawable/backgroud_blueline"         android:clickable="true"         android:gravity="center"         android:orientation="vertical"         android:visibility="gone">          <TextView             android:layout_width="wrap_content"             android:layout_height="wrap_content"             android:text="@string/msgWaitForSecond"/>          <ProgressBar             style="?android:attr/progressBarStyle"             android:layout_width="wrap_content"             android:layout_height="wrap_content"             android:indeterminateDrawable="@drawable/circle_progress"             android:indeterminateDuration="1000" />     </LinearLayout>      <Spinner         android:id="@+id/spAclass"         android:layout_width="match_parent"         android:layout_height="0dp"         android:layout_weight="2"         android:theme="@style/SpinnerTheme" />      <Spinner         android:id="@+id/spBclass"         android:layout_width="match_parent"         android:layout_height="0dp"         android:layout_weight="2"         android:theme="@style/SpinnerTheme" />      <Spinner         android:id="@+id/spCclass"         android:layout_width="match_parent"         android:layout_height="0dp"         android:layout_weight="2"         android:theme="@style/SpinnerTheme" />      <LinearLayout         android:layout_width="match_parent"         android:layout_height="0dp"         android:layout_weight="2"         android:orientation="horizontal"         android:weightSum="10">          <EditText             android:id="@+id/editSearchKey"             android:layout_width="0dp"             android:layout_height="match_parent"             android:layout_weight="8"             android:ems="10"             android:hint="@string/msgSearchKey"             android:inputType="textPersonName" />          <ImageButton             android:id="@+id/btnSearch"             android:layout_width="0dp"             android:layout_height="match_parent"             android:layout_weight="1"             app:srcCompat="@drawable/ic_baseline_search_24" />          <ImageButton             android:id="@+id/btnMaps"             android:layout_width="0dp"             android:layout_height="match_parent"             android:layout_weight="1"             app:srcCompat="@drawable/ic_baseline_map_24" />     </LinearLayout>      <androidx.recyclerview.widget.RecyclerView         android:id="@+id/listData"         android:layout_width="match_parent"         android:layout_height="0dp"         android:layout_weight="22" /> </LinearLayout>

이렇게 디자인된 화면을 미리 보기를 해 보면

 

layout 미리보기

그 다음은 recycleview 을 그려낼 화면 adapter 을 만들어 보면 다음과 같이 구현이 될 수 있다.

import android.content.Context; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView;  import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView;  import com.billcoreatech.opdgang1127.MainActivity; import com.billcoreatech.opdgang1127.R; import com.billcoreatech.opdgang1127.databinding.BannerAdContainerBinding; import com.billcoreatech.opdgang1127.databinding.Jejufd6infoViewBinding; import com.google.android.gms.ads.AdView;  import java.util.List;  /**  * {@link RecyclerAdMobAdapter} 클래스.  * <p>어댑터는 {@link ViewHolder}의 항목에 대한 액세스를 제공합니다.  * 또는 {@link AdViewHolder}.</p>  */ public class RecyclerAdMobAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {     // 데이터 항목 보기 유형입니다.     private static final int DATA_ITEM_VIEW_TYPE = 0;      // 배너 광고 보기 유형입니다.     private static final int BANNER_AD_VIEW_TYPE = 1;      // 활동의 컨텍스트입니다.     private final Context context;      // The list of banner ads and menu items.     private final List<Object> recyclerViewItems;      // data item layout     Jejufd6infoViewBinding binding ;     // banner ad layout     BannerAdContainerBinding adBinding ;     String TAG = "RecyclerAdMobAdapter";      /**      * 이 예제 앱의 경우 recyclerViewItems 목록에는      * {@link JejuFD6infoBean} 및 {@link AdView} 유형.      */     public RecyclerAdMobAdapter(Context context, List<Object> recyclerViewItems) {         this.context = context;         this.recyclerViewItems = recyclerViewItems;     }      /**      * {@link ViewHolder} 클래스.      * 메뉴 항목 보기의 각 보기에 대한 참조를 제공합니다.      */     public class ViewHolder extends RecyclerView.ViewHolder {         TextView placeName ;         TextView categoryName ;         TextView phone ;         TextView roadAddressName ;         public ViewHolder(@NonNull View itemView) {             super(itemView);              placeName = binding.placeName ;             categoryName = binding.categoryName;             phone = binding.phone;             roadAddressName = binding.roadAddressName;              // item click 을 활용 하기 위해서             itemView.setOnClickListener(new View.OnClickListener() {                 @Override public void onClick(View v) {                     int pos = getAdapterPosition() ;                     if (pos != RecyclerView.NO_POSITION) {                         // 리스너 객체의 메서드 호출.                         if (mListener != null) {                             mListener.onItemClick(v, pos) ;                         }                     }                 }             });         }     }      /**      * {@link AdViewHolder} 클래스입니다.      */     public class AdViewHolder extends RecyclerView.ViewHolder {          AdViewHolder(View view) {             super(view);         }     }      /**      * 데이터의 갯수를 구하기      * @return      */     @Override     public int getItemCount() {         return recyclerViewItems.size();     }      /**      * 주어진 위치에 대한 보기 유형을 결정합니다.      *      */     @Override     public int getItemViewType(int position) {         return (position % MainActivity.ITEMS_PER_AD == 0) ? BANNER_AD_VIEW_TYPE                 : DATA_ITEM_VIEW_TYPE;     }      /**      * 메뉴 항목 보기 또는 배너 광고 보기에 대한 새 보기 만들기      * viewType을 기반으로 합니다. 이 메소드는 레이아웃 관리자에 의해 호출됩니다.      */     @Override     public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {         switch (viewType) {             case DATA_ITEM_VIEW_TYPE:                 RecyclerView.LayoutParams lp = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);                 binding = Jejufd6infoViewBinding.inflate((LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE));                 View view = binding.getRoot() ;                 view.setLayoutParams(lp);                 return new ViewHolder(view);             case BANNER_AD_VIEW_TYPE:                 // fall through             default:                 adBinding = BannerAdContainerBinding.inflate((LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE));                 View bannerLayoutView = adBinding.getRoot();                 return new AdViewHolder(bannerLayoutView);         }     }      /**      * 메뉴 항목 보기를 구성하는 보기의 내용을 대체하고      * 배너 광고 보기. 이 메소드는 레이아웃 관리자에 의해 호출됩니다.      */     @Override     public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {         int viewType = getItemViewType(position);         switch (viewType) {             case DATA_ITEM_VIEW_TYPE:                 ViewHolder viewHolder = (ViewHolder) holder;                 JejuFD6infoBean dataBean = (JejuFD6infoBean) recyclerViewItems.get(position);                 viewHolder.categoryName.setText(dataBean.getCategory_name());                 viewHolder.phone.setText(dataBean.getPhone());                 viewHolder.placeName.setText(dataBean.getPlace_name());                 viewHolder.roadAddressName.setText(dataBean.getRoad_address_name());                 break;             case BANNER_AD_VIEW_TYPE:                 // 실패로 끝나다             default:                 AdViewHolder bannerHolder = (AdViewHolder) holder;                 AdView adView = (AdView) recyclerViewItems.get(position);                 ViewGroup adCardView = (ViewGroup) bannerHolder.itemView; //                RecyclerView에 의해 재활용된 AdViewHolder는 다를 수 있습니다. //                이전에 이 위치에 사용된 것보다 인스턴스입니다. 지우다 //                다른 하위 보기의 AdViewHolder //                AdView가 연결되어 있고 이 위치에 대한 AdView가 //                이미 다른 재활용 AdViewHolder의 상위 항목이 있습니다.                  if (adCardView.getChildCount() > 0) {                     adCardView.removeAllViews();                 }                 if (adView.getParent() != null) {                     ((ViewGroup) adView.getParent()).removeView(adView);                 }                  // Add the banner ad to the ad view.                 adCardView.addView(adView);         }     }      // 리스너 객체 참조를 저장하는 변수     private RecyclerAdMobAdapter.OnItemClickListener mListener = null ;      // OnItemClickListener 리스너 객체 참조를 어댑터에 전달하는 메서드     public void setOnItemClickListener(RecyclerAdMobAdapter.OnItemClickListener listener) {         this.mListener = listener ;     }      public interface OnItemClickListener {         void onItemClick(View v, int position) ;     }  }

앞에서 말했던 예제 소스에서 recycleview adapter class 을 복사해서 내가 필요한 부분으로 수정을 진행 했다. 

이제까지 구현했던 adapter class 와 다른 것은 viewHolder 을 2가지로 구성해서 한가지는 화면에 보여줄 데이터가 표시되는 viewHolder 을 만들고, 또 하나는 광고 banner 가 들어가는 adViewHolder 을 구현하는 것이다. 

 

그리고 viewType 을 이용해서 각각 뷰에 필요한 데이터를 넣어주는 방식으로 구현이 되는 것을 볼 수 있었다.  추가적으로 item 을 클릭했을 때 처리를 하기 위해서 onItemClickListener 을 구현해 주는 것이다. listview에서는 item에 onClick 리스너가 들어가서 클릭했을 때 처리를 할 수 있었으나, recycleview 에선 item 의 클릭을 인식을 구현할 수 없으므로 adapter의 onClick 리스너를 구현해서 처리를 하는 것이다. 

 

이제 MainActivity 에서 데이터와 adBanner 을 넣는 것을 구현해 보자.

import android.annotation.SuppressLint; import android.content.Context;  .....  public class MainActivity extends AppCompatActivity {      // recycleview 에서 광고가 보여질 간격     public static final int ITEMS_PER_AD = 8;     private static final String TAG = "MainActivity";     ActivityMainBinding binding ;     Updatejejufd6infoViewBinding fd6Binding ;     AppsInfoViewBinding infoViewBinding ;     ArrayList<String> listSpinnerA ;     ArrayList<String> listSpinnerB;     ArrayList<String> listSpinnerC;      DBHandler dbHandler ;     ArrayList<JejuFD6infoBean> dataBeans ;     JejuFD6Adapter adapter ;     // 수정한 recycleview 용 adapter      RecyclerAdMobAdapter recyclerAdMobAdapter ;     // adapter 로 전달할 데이터 구조체      //     안에 들어가는 정보가 2가지 형식이라 object 로 선언한 것 같음.     List<Object> recyclerViewItems ;      SharedPreferences sp ;     SharedPreferences.Editor editor ;     private final long FINISH_INTERVAL_TIME = 2000;     private long backPressedTime = 0;     public static Context mContext ;      // 광고 요청을 위해서     AdRequest adRequest ;      @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         binding = ActivityMainBinding.inflate(getLayoutInflater());         setContentView(binding.getRoot());          /**          * 이건 다른 class 에서 요기 있는 함수를 사용해 보기 위해서          */         mContext = this ;          // 광고 초기화를 꼭 처리 해야 한다.         MobileAds.initialize(this, new OnInitializationCompleteListener() {             @Override             public void onInitializationComplete(InitializationStatus initializationStatus) {                 Log.e(TAG, "adMob Ready !!!");             }         });          // 광고 요청을 선언 하고.         adRequest = new AdRequest.Builder().build();          // adapter 로 보낼 데이터 초기화         recyclerViewItems = new ArrayList<>();          // adapter 을 선언         recyclerAdMobAdapter = new RecyclerAdMobAdapter(MainActivity.this, recyclerViewItems);          .....          // item 클릭을 했을 때 처리하기 위한 구현 부분         recyclerAdMobAdapter.setOnItemClickListener(new RecyclerAdMobAdapter.OnItemClickListener() {             @Override             public void onItemClick(View v, int position) {                 Object data = recyclerViewItems.get(position);                 Log.e(TAG, "pos=" + position) ;                 if (!data.equals(JejuFD6infoBean.class)) {                     JejuFD6infoBean dataBean = (JejuFD6infoBean) data;                     Intent intent = new Intent(MainActivity.this, MapsActivity.class);                     Bundle extras = new Bundle();                     extras.putString("viewOption", "placePoint");                     extras.putString("placeName", dataBean.getPlace_name());                     extras.putString("y", dataBean.getY());                     extras.putString("x", dataBean.getX());                     intent.putExtras(extras);                     startActivityIfNeeded( intent, 100) ;                 } else {                     Log.e(TAG, "" + data.equals(JejuFD6infoBean.class));                 }             }         });         doInfoView();     }      '''''      @SuppressLint("Range")     public void doInfoDataView(String categoryName) {         // adapter 로 전달해 구조체를 지운다음.         recyclerViewItems.clear();         dbHandler = DBHandler.open(getApplicationContext());         Cursor rs = dbHandler.selectCategoryName(categoryName);         while (rs.moveToNext()) {             JejuFD6infoBean dataBean = new JejuFD6infoBean();             dataBean.setPlace_name(rs.getString(rs.getColumnIndex("place_name")));             dataBean.setPhone(rs.getString(rs.getColumnIndex("phone")));             dataBean.setRoad_address_name(rs.getString(rs.getColumnIndex("road_address_name")));             dataBean.setCategory_name(rs.getString(rs.getColumnIndex("category_name")));             dataBean.setUrl(rs.getString(rs.getColumnIndex("url")));             dataBean.setY(rs.getString(rs.getColumnIndex("y")));             dataBean.setX(rs.getString(rs.getColumnIndex("x")));             // 읽어온 데이터를 adapter 용 구조체에 일단 추가              recyclerViewItems.add(dataBean);         }         // 광고를 추가 하는 부분들         addBannerAds();         loadBannerAds();         // recycleview 에 adapter 을 설정         binding.listData.setAdapter(recyclerAdMobAdapter);         // recycleview 의 모양을 리스트형태로 설정         binding.listData.setLayoutManager(new LinearLayoutManager(getApplicationContext()));     }      .....      // 광고 배너를 추가 하기.     private void addBannerAds() {         // ITEMS_PER_AD 는 상수선언을 통해서 광고가 반복될 간격을 정한 값         for (int i = 0; i <= recyclerViewItems.size(); i += ITEMS_PER_AD) {             final AdView adView = new AdView(MainActivity.this);             adView.setAdSize(AdSize.BANNER);             adView.setAdUnitId(getString(R.string.BANNER_ID));             recyclerViewItems.add(i, adView);         }     }      /**      * 광고를 loading      */     private void loadBannerAds() {         // Load the first banner ad in the items list (subsequent ads will be loaded automatically         // in sequence).         loadBannerAd(0);     }      /**      * 광고를 loading      */     private void loadBannerAd(final int index) {          if (index >= recyclerViewItems.size()) {             return;         }          Object item = recyclerViewItems.get(index);         if (!(item instanceof AdView)) {             throw new ClassCastException("Expected item at index " + index + " to be a banner ad"                     + " ad.");         }          final AdView adView = (AdView) item;          // Set an AdListener on the AdView to wait for the previous banner ad         // to finish loading before loading the next ad in the items list.         adView.setAdListener(                 new AdListener() {                     @Override                     public void onAdLoaded() {                         super.onAdLoaded();                         // The previous banner ad loaded successfully, call this method again to                         // load the next ad in the items list.                         loadBannerAd(index + ITEMS_PER_AD);                     }                      @Override                     public void onAdFailedToLoad(LoadAdError loadAdError) {                         // The previous banner ad failed to load. Call this method again to load                         // the next ad in the items list.                         String error =                                 String.format(                                         "domain: %s, code: %d, message: %s",                                         loadAdError.getDomain(), loadAdError.getCode(), loadAdError.getMessage());                         Log.e(                                 "MainActivity",                                 "The previous banner ad failed to load with error: "                                         + error                                         + ". Attempting to"                                         + " load the next banner ad in the items list.");                         loadBannerAd(index + ITEMS_PER_AD);                     }                 });          // Load the banner ad.         adView.loadAd(new AdRequest.Builder().build());     } }

앞에서 말한 예제 소스에서 필요한 부분만 내가 만든 MainActivity 에 추가해 보았다.  이해를 하면 쉬워 보이기는 하는데,  일단 항목을 넣을 list 을 하다 선언하고 그 안에 데이터를 먼저 채운 다음 광고를 중간에 삽입 하는 그런 형식으로 추가가 되는 것으로 이해가 되었다. 실제 동작을 시켜 봐도 그렇게 이해가 될 수 있을 것 같다. 

 

 

 

실행해 보니 대충 이해가 되는 모양이 되었다.    이것으로 또 하나 배움(?)을 전하며...

댓글

이 블로그의 인기 게시물

이번주 로또 당첨 번호을 알려 드립니다.

Good Lock !!! 참조 site 티스토리 ## 로또 번호 예측 분석 및 5개 조합 제안 (자세한 설명 포함) 제공하신 1147회차부터 1167회차까지의 로또 당첨 번호 데이터를 분석하여 다음 회차(1168회차)의 예상 번호 조합 5개를 제시합니다. 분석은 제시된 6가지 통계적 패턴을 기반으로 이루어지며, 각 패턴의 주기성과 이전 회차와의 비교를 통해 예측합니다. 마지막 10회차 당첨 번호와 중복되지 않는 조합을 우선적으로 제시합니다. **1. 분석 방법:** 각 회차의 당첨 번호 6개 (7개 중 마지막 숫자 제외)를 사용하여 다음과 같은 통계 분석을 수행합니다. * **연속 번호 간격:** 연속된 번호가 나타날 때 그 사이의 간격을 계산합니다. (예: 1, 2, 4의 경우 간격은 1, 2입니다.) * **홀짝 개수 및 간격:** 홀수와 짝수의 개수를 세고, 홀수와 짝수가 번갈아 나오는 간격을 계산합니다. * **총합 및 총합 간격:** 각 회차의 번호 총합을 계산하고, 같은 총합이 이전에 나타났던 회차까지의 간격을 구합니다. * **평균 및 평균 간격:** 각 회차의 번호 평균을 계산하고, 같은 평균이 이전에 나타났던 회차까지의 간격을 구합니다. * **일치율 및 일치율 간격:** 위 1~4번의 결과들을 종합하여 일치율을 계산하고, 같은 일치율이 이전에 나타났던 회차까지의 간격을 구합니다. (일치율 계산은 각 지표의 비율을 종합적으로 고려하는 방식으로, 단순한 수치 합산이 아닌, 전문적인 통계 기법이 필요할 수 있습니다. 이 예시에서는 간략화된 추세 분석 방식을 사용합니다.) **2. 데이터 분석 및 패턴 발견 (간략화):** 제공된 데이터의 양이 많지 않고, 복잡한 통계 기법을 적용하기에는 제한적이므로, 간략화된 추세 분석을 통해 주요 패턴을 파악합니다. 실제 분석에서는 더욱 정교한 통계 기법 (예: 시계열 분석, 마르코프 체인 등)을 적용해야 더 정확한 예측이 가능합니다. **3. 예상 번호 조합 제...

이번주 로또 당첨 번호을 알려 드립니다.

Good Lock !!! 참조 site 티스토리 제공된 데이터를 분석하여 다음과 같은 통계 분석 결과 및 예상 번호 조합을 제시합니다. 데이터 양이 제한적이므로 통계적 유의미성은 낮을 수 있습니다. 이는 단순한 예측이며, 실제 당첨 결과와 다를 수 있음을 유의해야 합니다. **1. 연속 번호 간격:** 각 회차의 6개 번호에 대해 연속 번호의 간격을 계산합니다. (예: 1, 2, 4, 6, 8, 10 의 경우 간격은 1, 2, 2, 2, 2 입니다.) **2. 짝수/홀수 개수 및 간격:** 각 회차의 짝수와 홀수의 개수와 그 간격을 계산합니다. **3. 합계 및 합계 간격:** 각 회차의 합계를 계산하고, 같은 합계가 이전에 나타난 회차와의 간격을 계산합니다. **4. 평균 및 평균 간격:** 각 회차의 평균을 계산하고, 같은 평균이 이전에 나타난 회차와의 간격을 계산합니다. **5. 매칭 점수 및 매칭 비율 및 간격:** 위 1~4의 결과를 비교하여 매칭 점수와 매칭 비율을 계산하고, 같은 매칭 비율이 이전에 나타난 회차와의 간격을 계산합니다. 매칭 점수 계산은 4개 항목 모두 일치할 경우 4점, 3개 일치 시 3점, ... 식으로 계산됩니다. 매칭 비율은 (매칭 점수 / 4) * 100 입니다. **6. 다음 회차 예상 번호:** 위 분석 결과를 바탕으로 다음 회차에 나올 가능성이 높은 번호를 예측합니다. 이 과정은 주로 빈도 분석, 연속성, 간격 패턴 등을 고려합니다. 데이터의 제한으로 인해 단순한 빈도 분석에 의존하게 됩니다. **7. 최빈출 번호 조합:** 1부터 45까지의 번호 중 가장 자주 출현한 6개의 번호를 조합합니다. **8. 5개의 예상 번호 조합:** (아래에 5개 조합 제시 및 각 조합에 대한 설명) **(아래는 실제 데이터 분석 결과가 없으므로 가상의 예시입니다. 실제 데이터 분석을 수행해야 정확한 결과를 얻을 수 있습니다.)** **예시 (가상 데이터 기반):** * **최빈출 번호 조합:*...