반응형
포인트 리스트를 가지고 그림을 드로잉하고, 이것을 원하는 크기로 Resize 하고, 투명 이미지 datauri 만들기...
pointlist = '[(10,10)(20,20)(40,50)(60,80)][(10,80)(24,70)(40,60)(60,40)]'

# image 필드에 데이터가 없는 목록만 뽑아서 배치 작업을 수행한다.
def to_datauri(sn):
    sn = sn.strip()
    # 정규표현식으로 포맷 문자열에서 점 정보 추출
    point_regex = r"\(([-+]?\d+),([-+]?\d+)\)"
    strokes = sn.replace('[', '')
    strokes = strokes.split(']')
    strokes = [ s for s in strokes if s!='' ]

    # 그래프 그리기
    plt.figure()
    fig, ax = plt.subplots(figsize=(3, 2))
    ax.set_facecolor('none') # 배경 투명
    fig.patch.set_alpha(0) # 배경 투명
    # 축과 눈금 숨기기
    ax.axis('off')

    # 각 획별로 분리하여 그리기
    for stroke in strokes:
        points = re.findall(point_regex, stroke)
        if len(points)==0:
            continue
        x_list = [int(point[0]) for point in points]
        y_list = [int(point[1]) for point in points]
        ax.plot(x_list, y_list, color='black', lw=2)

    plt.gca().invert_yaxis()  # y축 뒤집기

    # 그래프 저장
    buf = io.BytesIO()
    plt.savefig(buf, format='png', bbox_inches='tight')
    buf_resized = buf

    ## resize 작업
    buf.seek(0)
    img = Image.open(buf)
    img_resized = img.resize((150,100), Image.Resampling.LANCZOS)  
    # img_resized = img.resize((150,100), Image.ANTIALIAS)  
    # sampling=Image.ANTIALIAS deprecated 되었다. 버전에 따라 위 둘 중 하나를 사용.
    buf_resized = io.BytesIO()
    img_resized.save(buf_resized, format=img.format)

    # buf대신 buf_resized를 변환
    buf_resized.seek(0)
    data_uri = 'data:image/png;base64,'+base64.b64encode(buf_resized.read()).decode('utf-8')
    plt.close()
    plt.clf()
    plt.close('all')
    img.close()
    img_resized.close()
    buf.close()
    buf_resized.close()

    return data_uri

이 함수를 사용하여 드로잉 데이터를 넣으면, datauri 텍스트가 출력된다.

시작,끝 따옴표를 제거한 텍스트를 복사해서 브라우저 URL창에 넣으면 드로잉된 투명이미지가 나온다.. (드래그해 보면, 선만 움직이는 것을 확인할 수 있다. 배경투명)

y축을 뒤집은 이유는? 모니터 스크린 좌표계에서 보통 좌상단이 (0,0) 이다.  plot에서는 좌하단이 (0,0)이다. 따라서 스크린 좌표계로 보이게 하려고 하였다. 

datauri가 아니라 파일로 저장하려면 plt.close() 전에  plt.savefig('test.png') 로 저장하면 된다.

 

'Python' 카테고리의 다른 글

투명 배경 이미지 만들기  (0) 2023.03.22
set에 set 추가? frozenset  (1) 2021.02.24
Jupyter Notebook 소스 복구  (0) 2020.06.16
Docker python venv 패키지 유지  (0) 2020.06.07
딕셔너리에서 키삭제  (0) 2019.12.07
반응형
ViewPager가 ViewPager2 로 업데이트되면서 사용하는 방법이 바뀌었다.

상단에 탭 메뉴가 있어서 메뉴 클릭 또는 컨텐트를 좌우 슬라이드하며 다른 페이지로 변경한다.

- TabLayout을 쓰기 위해 Dependency에 별다르게 추가하지 않아도 기본으로 material이 들어가있다. 

'com.google.android.material:material:1.7.0'

- main activity layout에 tab layout 과 viewpager2 를 추가

<?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:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tablayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
    >

        <com.google.android.material.tabs.TabItem
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Monday" />

        <com.google.android.material.tabs.TabItem
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Tuesday" />

        <com.google.android.material.tabs.TabItem
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Wednesday" />
    </com.google.android.material.tabs.TabLayout>

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        />

</LinearLayout>

- 각 탭별로 fragment xml 생성

frag_monday.xml, frag_tuesday.xml, frag_wednesday.xml 약간씩 바꿔서 확인.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="170dp"
        android:layout_marginTop="349dp"
        android:layout_marginEnd="170dp"
        android:text="Monday"
        android:textSize="20sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

- 각 fragment 클래스 생성. 이것도 이름만 약간씩 다르게..


public class FragMonday extends Fragment {
    private View view ;
    public static FragMonday newInstance() {
        FragMonday fragMonday = new FragMonday();
        return fragMonday;
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        view = inflater.inflate(R.layout.frag_monday, container,  false) ;
        return view ;
    }

}

- Adapter를 만들어야 한다. ViewPagerAdapter class


public class ViewPagerAdapter extends FragmentStateAdapter {
    private final List<Fragment> fragmnets = new ArrayList<Fragment>() ;
    private final List<String> titles = new ArrayList<>() ;

    public ViewPagerAdapter(@NonNull FragmentActivity fragmentActivity) {
        super(fragmentActivity);
    }

    @NonNull
    @Override
    public Fragment createFragment(int position) {
        return fragmnets.get(position) ;
    }

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

    public void addFragment(@NonNull Fragment frag, String title) {
        fragmnets.add(frag) ;
        titles.add(title) ;
    }
    @NonNull
    public String getTitle(int position) {
        return titles.get(position) ;
    }

}

 

- 이제 MainActivity에서 모두 연결시킨다.


public class MainActivity extends AppCompatActivity {

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

        ViewPagerAdapter viewPagerAdapter = new ViewPagerAdapter(this);
        viewPagerAdapter.addFragment(FragMonday.newInstance(), "Monday");
        viewPagerAdapter.addFragment(FragTuesday.newInstance(), "Tuesday");
        viewPagerAdapter.addFragment(FragWednesday.newInstance(), "Wednesday");

        ViewPager2 viewPager = findViewById(R.id.viewpager);
        viewPager.setAdapter(viewPagerAdapter);

        TabLayout tabLayout = findViewById(R.id.tablayout);
        TabLayoutMediator tm = new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> {
            tab.setText(viewPagerAdapter.getTitle(position)) ;
        }) ;
        tm.attach();
    }
}

끝.

 

'Develop > Android' 카테고리의 다른 글

RecyclerView, Firebase DB, reload  (0) 2023.03.19
[Android] JSON 데이터 송수신  (1) 2023.03.18
반응형
Firebase DB를 사용하여 RecyclerView에 출력하고, 갱신 버튼을 눌러, DB를 reloading하여 출력해 보자.

 

1. dependency 추가

implementation 'androidx.recyclerview:recyclerview:1.3.0'
implementation 'com.github.bumptech.glide:glide:4.10.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.10.0'

2. activity_main.xml에 recyclerview 추가. reload 버튼 추가

<Button
    android:id="@+id/btn_Reload"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="reload"
    />

<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/recyclerview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    />

3. 아이템 디자인 list_item.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">
    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <ImageView
            android:id="@+id/iv_profile"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:src="@mipmap/ic_launcher"
            />
        <LinearLayout
            android:layout_marginLeft="15dp"
            android:gravity="center_vertical"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:orientation="vertical"
            >
            <TextView
                android:id="@+id/tv_id"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="hello"/>
            <TextView
                android:id="@+id/tv_pw"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="hello"/>
            <TextView
                android:id="@+id/tv_userName"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="hello"/>
        </LinearLayout>
    </LinearLayout>
</LinearLayout>

4. 아이템 class. User class (getter and setter 는  alt+insert키로 쉽게 자동생성 하세요.. )


public class User {
    private String profile ;
    private String id ;
    private int pw ;
    private String userName ;

5. CustomAdapter class

public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.CustomViewHolder> {
    private ArrayList<User> arrayList ;
    private Context context ;

    public CustomAdapter(ArrayList<User> arrayList, Context context) {
        this.arrayList = arrayList;
        this.context = context;
    }

    @NonNull
    @Override
    public CustomAdapter.CustomViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item, parent, false) ;
        CustomViewHolder holder = new CustomViewHolder(view) ;
        return holder; // 홀더 리턴
    }

    @Override
    public void onBindViewHolder(@NonNull CustomAdapter.CustomViewHolder holder, int position) {
        Glide.with(holder.itemView)
                .load(arrayList.get(position).getProfile())
                .into(holder.iv_profile) ;
        holder.tv_id.setText(arrayList.get(position).getId());
        holder.tv_pw.setText(String.valueOf(arrayList.get(position).getPw()));
        holder.tv_userName.setText(arrayList.get(position).getUserName());
    }

    @Override
    public int getItemCount() {
        return (arrayList!=null ? arrayList.size(): 0);
    }

    public class CustomViewHolder extends RecyclerView.ViewHolder {
        ImageView iv_profile ;
        TextView tv_id ;
        TextView tv_userName ;
        TextView tv_pw ;

        public CustomViewHolder(@NonNull View itemView) {
            super(itemView);
            iv_profile = itemView.findViewById(R.id.iv_profile) ;
            tv_id = itemView.findViewById(R.id.tv_id) ;
            tv_userName = itemView.findViewById(R.id.tv_userName) ;
            tv_pw = itemView.findViewById(R.id.tv_pw) ;

        }
    }
}

6. Firebase 설정.

Tools - firebase 선택.  (예전엔 수작업으로 한 거를 편리하게 다 해준다..)

우측에 Realtime Database 메뉴를 선택한다.  Get started with Realtime Database 로 가서, 차례로 진행한다.

connect your app to firebase ; 브라우저로 구글 로그인하여 firebase 콘솔로 가서 프로젝트 생성까지 진행됨.

(실시간 DB를 잘 만든다. 필드도  User class에 맞게 다 맞춘다. +를 눌러 User를 만들고 다시 +를 눌러 User_01을 만들고 다시 +를 눌러 profile, id, pw, userName 필드들을 만든다.   처음엔 사용법을 몰라 어렵다. 하위 노드를 위해 미리 +를 연속해서 눌러줘야 된다. 미리 만들고, 엔터치면 하위에 추가가 안되서 삽질.. )

Add the Realtime Database to your app ; dependency 등을 알아서 설정해 준다.

그러나,... 나의 경우는 빌드시 에러가...  찾아보니, 버전 문제가 또 있다... (어쩌라고.. 정말  막막하게 만드는  버전 호환성...)

build.gradle (project) 에서 4.3.10 으로 들어가 있는데 찾아보니 버전을 14로 올리면 해결된다고 해서 해보니 성공하였음.

classpath 'com.google.gms:google-services:4.3.14'

 

7.MainActivity에서 RecyclerView와 Adapter 연결 및 Firebase 연동...

public class MainActivity extends AppCompatActivity {
    private RecyclerView recyclerView ;
    private RecyclerView.Adapter adapter ;
    private RecyclerView.LayoutManager layoutManager ;
    private ArrayList<User> arrayList ;
    private FirebaseDatabase database ;
    private DatabaseReference databaseReference ;
    private ValueEventListener valueEventListener ;


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

        recyclerView = findViewById(R.id.recyclerview) ;
        recyclerView.setHasFixedSize(true);
        layoutManager = new LinearLayoutManager(this) ;
        recyclerView.setLayoutManager(layoutManager);
        arrayList = new ArrayList<User>() ;

        adapter = new CustomAdapter(arrayList, this) ;
        recyclerView.setAdapter(adapter);

        database = FirebaseDatabase.getInstance();
        databaseReference = database.getReference("User") ; // User table

        valueEventListener = new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot snapshot) {
                Log.e("AAA", "reload data") ;
                // 데이터를 수신.
                arrayList.clear();
                for (DataSnapshot ds : snapshot.getChildren()) {
                    User user = ds.getValue(User.class) ;
                    Log.e("AAA", user.getId()) ;
                    arrayList.add(user) ;
                }
                adapter.notifyDataSetChanged();
            }

            @Override
            public void onCancelled(@NonNull DatabaseError error) {
                Log.e("AAA", error.toString()) ;
            }
        } ;

        databaseReference.addListenerForSingleValueEvent(valueEventListener);

        findViewById(R.id.btn_Reload).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                databaseReference.addListenerForSingleValueEvent(valueEventListener);
            }
        });

    }
}

- Reload를 어떻게 하나 궁금했는데 찾아보니 위와 같이 하니 되었다. databaseReference에 리스너를 또 등록하면 됨. 그러면 노드들을 다시 가져오고 갱신되었다. 

 

'Develop > Android' 카테고리의 다른 글

ViewPager2, TabLayout  (0) 2023.03.19
[Android] JSON 데이터 송수신  (1) 2023.03.18

+ Recent posts